一、方法内的变量是线程安全
非线程安全是对于 “实例变量而言的”,对于方法内的变量,不存在线程安全问题
package com.it.po.thread04; public class GetNum { public void getNum(String username){ try { int num=0; if("a".equals(username)){ num=100; Thread.sleep(1000); }else { num=200; } System.out.println("名称:username= "+username+" num= "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread01_2 extends Thread { private GetNum getNum; public MyThread01_2(GetNum getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("b"); } }
package com.it.po.thread04; public class MyThread01_1 extends Thread { private GetNum getNum; public MyThread01_1(GetNum getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("a"); } }
package com.it.po.thread04; public class Run01 { public static void main(String[] args){ GetNum getNum = new GetNum(); MyThread01_1 my1 = new MyThread01_1(getNum); MyThread01_2 my2 = new MyThread01_2(getNum); my1.start(); my2.start(); } }
二、实例变量非线程安全
(一)两个线程访问没有同步方法的实例变量
如果多个线程访问一个对象的实例变量,则可能出现线程安全问题
package com.it.po.thread04; public class GetNum2 { private int num=0; public void getNum(String username){ try { if("a".equals(username)){ num=100; Thread.sleep(2000); }else { num=200; } System.out.println("名称:username= "+username+" num= "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread02_1 extends Thread { private GetNum2 getNum; public MyThread02_1(GetNum2 getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("a"); } }
package com.it.po.thread04; public class MyThread02_2 extends Thread { private GetNum2 getNum; public MyThread02_2(GetNum2 getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("b"); } }
package com.it.po.thread04; public class Run02 { public static void main(String[] args){ GetNum2 getNum = new GetNum2(); MyThread02_1 my1 = new MyThread02_1(getNum); MyThread02_2 my2 = new MyThread02_2(getNum); my1.start(); my2.start(); } }
GetNum2 中只有一个变量会被覆盖,多个变量会出现交叉的情况。
(二)、两个线程访问同步方法的实例变量(上面的例子中GetNum2 方法添加synchronized )
package com.it.po.thread04; public class GetNum2 { private int num=0; synchronized public void getNum(String username){ try { if("a".equals(username)){ num=100; Thread.sleep(2000); }else { num=200; } System.out.println("名称:username= "+username+" num= "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }
名称:username= b num= 200
名称:username= a num= 100
这里情况是线程安全的。
三、多个对象多个锁
package com.it.po.thread04; public class GetNum3 { private int num=0; synchronized public void getNum(String username){ try { if("a".equals(username)){ num=100; System.out.println("a set over..."); Thread.sleep(2000); }else { num=200; System.out.println("b set over..."); } System.out.println("名称:username= "+username+" num= "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread03_1 extends Thread { private GetNum3 getNum; public MyThread03_1(GetNum3 getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("a"); } }
package com.it.po.thread04; public class MyThread03_2 extends Thread { private GetNum3 getNum; public MyThread03_2(GetNum3 getNum) { this.getNum = getNum; } @Override public void run() { super.run(); getNum.getNum("b"); } }
package com.it.po.thread04; public class Run03 { public static void main(String[] args){ GetNum3 getNum1 = new GetNum3(); GetNum3 getNum2 = new GetNum3(); MyThread03_1 my1 = new MyThread03_1(getNum1); MyThread03_2 my2 = new MyThread03_2(getNum2); my1.start(); my2.start(); } }

两个线程分别访问同一个类的不同实例的相同名称的方法,效果却是异步方式运行的,
上面创建了两个业务对象,产生了两个锁。
注意:
1. synchronized取得的锁都是对象锁,而不是把一段代码或是方法的当成锁。
2.一般哪个线程先执行带synchronized的方法,哪个线程就持有该对象作为锁,这只是对于该对象
只有一个锁的情况下。
同步的单词:synchronized
异步的单词:asynchronized.
四、synchronized方法与锁对象
证明synchronized锁的是对象
package com.it.po.thread04; public class MyObject { public void methodA(){ try { System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。。"); Thread.sleep(3000); System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread04_1 extends Thread { private MyObject myObject; public MyThread04_1(MyObject myObject) { this.myObject = myObject; } @Override public void run() { super.run(); myObject.methodA(); } }
package com.it.po.thread04; public class MyThread04_2 extends Thread { private MyObject myObject; public MyThread04_2(MyObject myObject) { this.myObject = myObject; } @Override public void run() { super.run(); myObject.methodA(); } }
package com.it.po.thread04; public class Run04 { public static void main(String[] args){ MyObject myObject = new MyObject(); MyThread04_1 my1 = new MyThread04_1(myObject); MyThread04_2 my2 = new MyThread04_2(myObject); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程:A 进来了。。。
线程:B 进来了。。。
线程:A 出去了。。。
线程:B 出去了。。。
修改 MyObject 在方法前加 synchronized
package com.it.po.thread04; public class MyObject { synchronized public void methodA(){ try { System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。。"); Thread.sleep(3000); System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程:A 进来了。。。
线程:A 出去了。。。
线程:B 进来了。。。
线程:B 出去了。。。
分析:资源对象只有一个,方法有 synchronized ,多线程需要排队。
牢牢记住:只有共享资源才需要同步化(比如加synchronized ),
如果不是共享资源,那么根本没有同步的必要
(二)一个类存在非同步方法情况
package com.it.po.thread04; public class MyObject1 { synchronized public void methodA(){ try { System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 进来了。。。"); Thread.sleep(3000); System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 出去了。。。"); System.out.println("methodA time= "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } public void methodB(){ try { System.out.println("methodB 线程:"+Thread.currentThread().getName()+" 进来了。。time= "+ System.currentTimeMillis()); Thread.sleep(3000); System.out.println(" methodB 线程:"+Thread.currentThread().getName()+" 出去了。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread05_1 extends Thread { private MyObject1 myObject; public MyThread05_1(MyObject1 myObject) { this.myObject = myObject; } @Override public void run() { super.run(); myObject.methodA(); } }
package com.it.po.thread04; public class MyThread05_2 extends Thread { private MyObject1 myObject; public MyThread05_2(MyObject1 myObject) { this.myObject = myObject; } @Override public void run() { super.run(); myObject.methodB(); } }
package com.it.po.thread04; public class Run05 { public static void main(String[] args){ MyObject1 myObject = new MyObject1(); MyThread05_1 my1 = new MyThread05_1(myObject); MyThread05_2 my2 = new MyThread05_2(myObject); my1.setName("C"); my2.setName("D"); my1.start(); my2.start(); } }
methodB 线程:D 进来了。。time= 1573568524394
methodA 线程:C 进来了。。。
methodB 线程:D 出去了。。。
methodA 线程:C 出去了。。。
methodA time= 1573568527395
结论,方法B虽然不是同步方法,但是线程可以不用排队。线程D异步调用方法B
methodB 方法加上 synchronized
package com.it.po.thread04; public class MyObject1 { synchronized public void methodA(){ try { System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 进来了。。。"); Thread.sleep(3000); System.out.println("methodA 线程:"+Thread.currentThread().getName()+" 出去了。。。"); System.out.println("methodA time= "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public void methodB(){ try { System.out.println("methodB 线程:"+Thread.currentThread().getName()+" 进来了。。time= "+ System.currentTimeMillis()); Thread.sleep(3000); System.out.println(" methodB 线程:"+Thread.currentThread().getName()+" 出去了。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } }
methodA 线程:C 进来了。。。 methodA 线程:C 出去了。。。 methodA time= 1573568844110 methodB 线程:D 进来了。。time= 1573568844110 methodB 线程:D 出去了。。
(三)synchronized 锁重入
当一个线程得到一个对象锁之后,再次请求此锁是可以再次得到该对象的锁的 。
在synchronized 的方法/块的内部调用本类的其他synchronized 是永远得到锁的。
package com.it.po.thread04; public class Service { synchronized public void service1(){ System.out.println("service1..."); service2(); } synchronized public void service2(){ System.out.println("service2..."); service3(); } synchronized public void service3(){ System.out.println("service3..."); } }
package com.it.po.thread04; public class MyThread06 extends Thread { @Override public void run() { super.run(); Service service = new Service(); service.service1(); } }
package com.it.po.thread04; public class Run06 { public static void main(String[] args){ MyThread06 myThread06 = new MyThread06(); myThread06.start(); } }
(四)synchronized 锁重入支持在父子类继承的环境
package com.it.po.thread04; public class Main { public int i = 10; synchronized public void operationMainMethod(){ try { i--; System.out.println("main print i= "+i); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class Sub extends Main { synchronized public void operationSubMethod() { try { while (i>0) { i--; System.out.println("Sub print i= " + i); Thread.sleep(100); this.operationMainMethod();//调用父类的 } } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread7 extends Thread { @Override public void run() { super.run(); Sub sub = new Sub(); sub.operationSubMethod(); } }
package com.it.po.thread04; public class Run07 { public static void main(String[] args){ MyThread7 myThread07 = new MyThread7(); myThread07.start(); } }
Sub print i= 9 main print i= 8 Sub print i= 7 main print i= 6 Sub print i= 5 main print i= 4 Sub print i= 3 main print i= 2 Sub print i= 1 main print i= 0
(五)、出现异常锁自动释放。
package com.it.po.thread04; public class Service2 { synchronized public void testmethod(){ System.out.println("线程:"+Thread.currentThread().getName()+" 进来了。。"); if("A".equals(Thread.currentThread().getName())){ System.out.println("线程A出现遇到异常。。。"); int i=1/0; } System.out.println("线程:"+Thread.currentThread().getName()+" 出去了。。"); } }
package com.it.po.thread04; public class MyThread8_1 extends Thread { private Service2 service2; public MyThread8_1(Service2 service2) { this.service2 = service2; } @Override public void run() { super.run(); service2.testmethod(); } }
package com.it.po.thread04; public class MyThread8_2 extends Thread { private Service2 service2; public MyThread8_2(Service2 service2) { this.service2 = service2; } @Override public void run() { super.run(); service2.testmethod(); } }
package com.it.po.thread04; public class Run08 { public static void main(String[] args){ Service2 service2 = new Service2(); MyThread8_1 my1 = new MyThread8_1(service2); MyThread8_2 my2 = new MyThread8_2(service2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
线程:A 进来了。。 线程A出现遇到异常。。。 Exception in thread "A" java.lang.ArithmeticException: / by zero at com.it.po.thread04.Service2.testmethod(Service2.java:8) at com.it.po.thread04.MyThread8_1.run(MyThread8_1.java:11) 线程:B 进来了。。 线程:B 出去了。。
注意:这种情况有给实例变量赋值,很有可能出现脏读情况。
(六)、同步不具有继承性
package com.it.po.thread04; public class Main2 { synchronized public void serviceMethod(){ try { System.out.println("Main2 线程:"+Thread.currentThread().getName()+" 进来了 " + "time= "+System.currentTimeMillis()); Thread.sleep(2000); System.out.println("Main2 线程:"+Thread.currentThread().getName()+" 出去了 " + "time= "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class Sub2 extends Main2{ @Override public void serviceMethod(){ try { System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 进来了 " + "time= "+System.currentTimeMillis()); Thread.sleep(2000); System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 出去了 " + "time= "+System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.it.po.thread04; public class MyThread9_1 extends Thread { private Sub2 sub2; public MyThread9_1(Sub2 sub2) { this.sub2 = sub2; } @Override public void run() { super.run(); sub2.serviceMethod(); } }
package com.it.po.thread04; public class MyThread9_2 extends Thread { private Sub2 sub2; public MyThread9_2(Sub2 sub2) { this.sub2 = sub2; } @Override public void run() { super.run(); sub2.serviceMethod(); } }
package com.it.po.thread04; public class Run09 { public static void main(String[] args){ Sub2 sub2 = new Sub2(); MyThread9_1 my1 = new MyThread9_1(sub2); MyThread9_2 my2 = new MyThread9_2(sub2); my1.setName("A"); my2.setName("B"); my1.start(); my2.start(); } }
Sub2 线程:A 进来了 time= 1573830321129 Sub2 线程:B 进来了 time= 1573830321129 Sub2 线程:B 出去了 time= 1573830323130 Sub2 线程:A 出去了 time= 1573830323130 Main2 线程:B 进来了 time= 1573830323130 Main2 线程:B 出去了 time= 1573830325130 Main2 线程:A 进来了 time= 1573830325130 Main2 线程:A 出去了 time= 1573830327130
修改成同步的
package com.it.po.thread04; public class Sub2 extends Main2{ @Override synchronized public void serviceMethod(){ try { System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 进来了 " + "time= "+System.currentTimeMillis()); Thread.sleep(2000); System.out.println("Sub2 线程:"+Thread.currentThread().getName()+" 出去了 " + "time= "+System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }
Sub2 线程:A 进来了 time= 1573830438039 Sub2 线程:A 出去了 time= 1573830440039 Main2 线程:A 进来了 time= 1573830440039 Main2 线程:A 出去了 time= 1573830442039 Sub2 线程:B 进来了 time= 1573830442039 Sub2 线程:B 出去了 time= 1573830444039 Main2 线程:B 进来了 time= 1573830444039 Main2 线程:B 出去了 time= 1573830446040
浙公网安备 33010602011771号