在多线程的环境下,常常存在线程安全问题,这种问题产生的缘由在于:该是原子操做的代码段被其余的线程切割,从而引发的数据混乱问题。在本篇博客中将讲述如何使用synchronized关键字保证代码段的原子操做。java
无论synchronized以何种方式使用,都会对一个对象加锁,这个对象也就是所谓的监视器安全
synchronized关键字具备一下特征:多线程
(1)若是对持有相同锁的synchronized方法或者代码块,同步执行(即排队,执行完一个,另外一个才能执行)异步
(2)若是对持有不一样锁的synchronized方法或者代码块,异步执行ide
(3)synchronized与非synchronized方法或者代码块,异步执行性能
使用synchronized来修饰方法(非static方法),其实就是this对象加锁测试
下面经过synchronized方法来看一下上述的三个特征优化
首先定义一个操做类MyObject,在类中有三个方法init,alter,print,其中init,alter方法使用synchronized修饰,代码以下:
this
package com.feng.example; public class MyObject { synchronized public void init() { try { System.out.println(Thread.currentThread().getName()+"init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"init end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } synchronized public void alter() { try { System.out.println(Thread.currentThread().getName()+"alter begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"alter end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void printInfo() { try { System.out.println(Thread.currentThread().getName()+"print begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"print end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
定义三个线程,分别对MyObject进行处理:spa
package com.feng.example; public class MyThreadA extends Thread{ private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { object.init(); } }
package com.feng.example; public class MyThreadB extends Thread{ private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { object.alter(); } }
package com.feng.example; public class MyThreadC extends Thread{ private MyObject object; public MyThreadC(MyObject object) { this.object = object; } @Override public void run() { object.printInfo(); } }
测试类以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); Thread a = new MyThreadA(object1); Thread b = new MyThreadB(object1); Thread c = new MyThreadC(object1); a.start(); b.start(); c.start(); } }
分析:在测试类中,三个线程都是以实例化对象object1做为参数,那么三个类处理的都是object1对象,由于MyThreadA,MyThreadB处理的object1对象的同步方法,他们持有的监视器都是object1对象,所以二者应该是同步的,即排队执行,MyThreadC处理的是非synchronized方法,所以和另外两个线程是异步执行的。因此init方法,和alter方法是顺序执行(即一个执行完,另外一个在执行,可是谁在前取决于谁先抢到object1的锁),print方法和init,alter方法是异步执行,能够在任意位置输出。
程序运行结果以下:
Thread-2print begin Thread-0init begin Thread-2print end Thread-0init end Thread-1alter begin Thread-1alter end
从本例中说明对持有相同锁的多个线程在执行synchronized方法时同步执行。synchronized方法与非synchronized方法异步执行。
下面修改测试类,观察若是线程处理的对象不是一个对象会出现什么状况:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); Thread a = new MyThreadA(object1); Thread b = new MyThreadB(object2); a.start(); b.start(); } }
分析:线程a操做的对象是object1,线程b操做的对象是object2。另外一层意思,线程a执行init方法是获取的是object1对象的锁,线程b执行alter方法时获取的是object2对象的锁。虽然两个方法都是synchronized方法,可是二者想要获取的锁不一样,所以也就不存在同步问题。
执行结果以下:
Thread-0init begin Thread-1alter begin Thread-0init end Thread-1alter end
从结果中能够看出,线程0中init的操做被线程1的alter操做分隔了,说明二者异步执行。
从而证实了synchronized方法持有的是this对象锁。对持有不一样锁对象的synchronized方法异步执行。
经过上面的实验证实了synchronized关键字的3个特征。
修改操做类,代码以下:
package com.feng.example; public class MyObject { synchronized public void init() { try { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); alter(); System.out.println(Thread.currentThread().getName()+"====init end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } synchronized public void alter() { try { System.out.println(Thread.currentThread().getName()+"====alter begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====alter end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void printInfo() { try { System.out.println(Thread.currentThread().getName()+"====print begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====print end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试类以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { MyObject object1 = new MyObject(); Thread a = new MyThreadA(object1); Thread b = new MyThreadB(object1); a.start(); Thread.sleep(1000); b.start(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:实例化操做类对象object1,建立两个线程a,b,a线程启动,执行init方法,线程a获取了object1对象的锁,在init方法方法中睡眠2s,执行init方法1s后启动线程b,b企图得到object1对象锁,因为被线程a占有,因此须要等待。2s事后init方法中调用alter方法,须要获取object1对象的锁,如今线程a已经得到了object1对象的锁,能够直接进入alter方法,这就叫课重入性。执行完alter方法以后,线程a释放锁,线程b再执行alter方法。
输出结果以下:
Thread-0====init begin Thread-0====alter begin Thread-0====alter end Thread-0====init end Thread-1====alter begin Thread-1====alter end
建立父类:
package com.feng.example; public class Person { synchronized public void eat() { System.out.println("person eat"); } }
建立子类:
package com.feng.example; public class Student extends Person{ synchronized public void study() { System.out.println("student study"); super.eat(); System.out.println("study end"); } }
测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { Student student = new Student(); Thread a = new MyThread(student); a.start(); } }
分析:启动线程a,a获取了student的锁,执行study方法,在study方法中调用父类的eat方法,不须要从新获取锁便可进入同步方法。
执行结果以下:
student study person eat study end
修改操做类:
package com.feng.example; public class MyObject { synchronized public void init() { try { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); throw new InterruptedException(); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } synchronized public void alter() { try { System.out.println(Thread.currentThread().getName()+"====alter begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====alter end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
线程类使用上面的MyThreadA,MyThreadB,此处再也不列出
测试类以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { try { MyObject object = new MyObject(); Thread a = new MyThreadA(object); Thread b = new MyThreadB(object); a.start(); Thread.sleep(1000); //此处是要保证线程a先执行 b.start(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:线程a执行首先获取object对象锁,执行init方法,执行完init时抛出异常,若是线程a不释放锁,那么线程b就不会执行。只要线程b执行了,就说明抛出异常,线程锁释放。
运行结果以下:
Thread-0====init begin 抛出异常 java.lang.InterruptedException at com.feng.example.MyObject.init(MyObject.java:11) at com.feng.example.MyThreadA.run(MyThreadA.java:15) Thread-1====alter begin Thread-1====alter end
说明线程a抛出异常时,释放了线程锁
不具备继承性主要体如今方法的重写上,对父类的同步方法进行重写,若是不加synchronized关键字就不是同步方法
经过程序验证上述结论:
package com.feng.example; public class Person { synchronized public void eat() { System.out.println("person eat"); } }
package com.feng.example; public class Student extends Person{ public void eat() { try { System.out.println("student eat..."); Thread.sleep(4000); System.out.println("student eat end..."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } synchronized public void study() { try { System.out.println("student study..."); Thread.sleep(2000); System.out.println("student study end..."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
定义两个线程:
package com.feng.example; public class StudentThreadA extends Thread{ private Student student; public StudentThreadA(Student student) { this.student = student; } @Override public void run() { // TODO Auto-generated method stub super.run(); student.eat(); } }
package com.feng.example; public class StudentThreadB extends Thread{ private Student student; public StudentThreadB(Student student) { this.student = student; } @Override public void run() { // TODO Auto-generated method stub super.run(); student.study(); } }
测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { Student student = new Student(); Thread a = new StudentThreadA(student); Thread b = new StudentThreadB(student); a.start(); b.start(); } }
执行结果以下:
student eat... student study... student study end... student eat end...
分析:从运行结果中能够看出两个方法并非同步输出的,所以重写的eat方法不是同步方法。
使用synchronized方法,存在明显的性能问题,在一个方法中可能只有几个语句须要同步,使用synchronized方法却使整个方法都同步了。执行速度确定会慢,咱们能够经过synchronized语句块来优化程序。
举例说明synchronized方法的性能问题。
定义操做类:
package com.feng.example; public class MyObject { private String data1; private String data2; synchronized public void init() { try { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); data1 = "长时间的处理后从服务端获取的值data1"; //模拟从服务端取数据 data2 = "长时间的处理后从服务端获取的值data2"; System.out.println(data1); System.out.println(data2); System.out.println(Thread.currentThread().getName()+"====init end"); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
定义线程类:
package com.feng.example; public class MyThread extends Thread { private MyObject object; public MyThread(MyObject object) { this.object = object; } @Override public void run() { System.out.println(Thread.currentThread().getName()+":开始运行时间为:"+System.currentTimeMillis()); object.init(); System.out.println(Thread.currentThread().getName()+":结束时间为:"+System.currentTimeMillis()); } }
测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThread(object); Thread b = new MyThread(object); a.start(); b.start(); } }
分析:在测试类中定义了两个线程实例a,b 这两个线程都以object实例做为参数,对object进行操做。首先线程a执行,输出线程a的开始执行时间,执行object对象的init方法,init方法是同步方法。在执行init的同时,线程b也启动了,输出线程b的开始执行时间,这时线程b企图获取object对象的锁,因为如今object的锁被线程a占用,因此必须等待线程a执行完init方法以后才会得到。线程a休眠2秒,这2s主要是模拟长时间的操做(假设就是从服务端获取数据),给成员变量赋值,打印两个成员变量,运行完成,释放锁。输出线程a的结束时间,线程b执行object对象的init的放,进行一些列的操做。
运行结果以下:
Thread-1:开始运行时间为:1449471254353 Thread-1====init begin Thread-0:开始运行时间为:1449471254366 长时间的处理后从服务端获取的值data1 长时间的处理后从服务端获取的值data2 Thread-1====init end Thread-1:结束时间为:1449471256364 Thread-0====init begin 长时间的处理后从服务端获取的值data1 长时间的处理后从服务端获取的值data2 Thread-0====init end Thread-0:结束时间为:1449471258365
查看这两个线程都完成操做所用的总时间就是Thread-0的结束时间减去Thread-1开始运行时间:1449471258365-1449471254353 = 4012 花费了大约4s的时间。可是想服务端获取数据彻底能够异步去执行,这样就能够减小执行时间了。
修改操做对象MyObject的代码,使用synchronized语句块
package com.feng.example; public class MyObject { private String data1; private String data2; public void init() { try { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); String data1Temp = "长时间的处理后从服务端获取的值data1"+Thread.currentThread().getName(); //模拟从服务端取数据 String data2Temp = "长时间的处理后从服务端获取的值data2"+Thread.currentThread().getName(); synchronized(this) { data1 = data1Temp; data2 = data2Temp; } System.out.println(data1); System.out.println(data2); System.out.println(Thread.currentThread().getName()+"====init end"); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
其线程类与测试类代码不变,分析程序:
在测试类中建立两个线程实例a,b 线程a与线程b同时执行object的init方法,因为此方法是非synchronized方法,所以,两只异步执行,(二者谁先执行到synchronized语句块不肯定)假设线程a先执行到synchronized语句块,线程a得到了进入synchronized语句块的锁,当线程b执行到synchronized语句块时,尝试获取锁,但要获得线程a执行完synchronized语句块,线程a执行完synchronized语句块后释放锁,线程b获取锁执行synchronized语句块的内容。
执行结果以下:
Thread-1:开始运行时间为:1449472151572 Thread-1====init begin Thread-0:开始运行时间为:1449472151577 Thread-0====init begin 长时间的处理后从服务端获取的值data1Thread-1 长时间的处理后从服务端获取的值data2Thread-1 Thread-1====init end Thread-1:结束时间为:1449472153572 长时间的处理后从服务端获取的值data1Thread-0 长时间的处理后从服务端获取的值data2Thread-0 Thread-0====init end Thread-0:结束时间为:1449472153577
从执行结果中能够看出,执行的总时间大约为2s,相比于使用synchronized方法快了不少。程序的性能获得了提高
总结:synchronized语句块就是将同步的内容最小化,使须要同步的信息放到synchronized语句块中同步执行,不须要同步的信息放到synchronized语句块外异步执行。
synchronized语句块的锁对象能够是任意的对象,不论是什么对象只要记住文章开头synchronized关机字的特征便可。
下面比较一下this,synchronized方法,class做为锁的区别:
首先synchronized(this){ } 使用的锁对象就是操做类自己,不如上例中在线程类中调用的是object.init()方法,说明init方法中的this指的就是object对象本身,所以使用this做为锁和synchronized方法没有本质的区别,区别只在于synchronized(this){ }能够将同步的范围缩小,是同步的内容更加精确,提升程序的性能。
sychronized(MyObject.class){ },是将class对象做为锁,同一个类的实例都只有一个class文件,class文件是惟一的,也就是说若是使用class做为锁,即便调用的是此类不一样实例对象的方法,都会呈现出同步的效果。
举例说明:
定义操做类MyObject
package com.feng.example; public class MyObject { public void init() { try { synchronized(this) { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
定义线程类:
package com.feng.example; public class MyThread extends Thread { private MyObject object; public MyThread(MyObject object) { this.object = object; } @Override public void run() { object.init(); } }
测试类代码以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); Thread a = new MyThread(object1); Thread b = new MyThread(object2); a.start(); b.start(); } }
分析:因为在MyObject类中使用的是this做为锁,在测试类中定义了两个MyObject对象object1,object2,实例化线程时使用两个不一样的对象做为参数。线程a调用的是object1.init()方法,线程b调用的是object2.init()方法。两个线程在执行object.init()方法时,this表明的就不是一个对象,因此是异步执行。
运行结果以下:
Thread-0====init begin Thread-1====init begin Thread-0====init end Thread-1====init end
下面修改MyObject操做类的代码:将synchronized(this){ } 改成synchronized(MyObject.class){ }
package com.feng.example; public class MyObject { public void init() { try { synchronized(MyObject.class) { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:两个线程a,b 线程a执行的是object1.init()方法,线程b执行的object2.init()方法,在执行到synchronized(MyObject.class){ }时,谁先执行到,谁就得到此类文件的class锁,因为object1和object2的class文件是同一个,所以两个线程在synchronized语句块处同步执行。
程序运行结果以下:
Thread-0====init begin Thread-0====init end Thread-1====init begin Thread-1====init end
只要是对象均可以做为锁,修改上例中的MyObject类:
package com.feng.example; public class MyObject { private String lock = new String(); public void init() { try { synchronized(lock) { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:两个线程共用一个object对象,object中lock是object的成员变量,也就是说两个线程共用一个锁。
所以两个线程在synchronized语句块处同步执行。
运行结果以下:
Thread-0====init begin Thread-1====init begin 长时间的处理后从服务端获取的值data1Thread-0 长时间的处理后从服务端获取的值data2Thread-0 Thread-0====init end 长时间的处理后从服务端获取的值data1Thread-1 长时间的处理后从服务端获取的值data2Thread-1 Thread-1====init end
字符串通常是存放在常量池中的,若是两个字符串表示的内容同样,两个字符串指向的是同一个对象。举例说明:
修改操做类MyObject代码
package com.feng.example; public class MyObject { public void init() { String str = "我是锁"; try { synchronized(str) { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:在init方法中str是一个局部变量,在方法内部局部变量都有本身的临时空间,各个方法互不干扰,按常理来讲,两个线程a,b应该想获取的锁不是同一个锁,可是这里是String类型,String类型的内容是放在常量池中的,在本程序中,str虽然是方法内的局部变量,但在不一样的方法中局部变量都是指向的同一个对象,所以表示的是同一个锁。
所以不建议使用字符串做为锁对象
程序运行结果以下:
Thread-0====init begin Thread-0====init end Thread-1====init begin Thread-1====init end
synchronized方法对应synchronized(this){ }语句块,一样的synchronized static 方法对应synchronized(class){ }语句块
举例说明:修改上述程序的操做类代码:
package com.feng.example; public class MyObject { synchronized static public void init() { try { System.out.println(Thread.currentThread().getName()+"====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"====init end"); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:两个线程虽然执行的是不一样对象的init方法,可是init方法是static的synchronized方法,两个对象的class文件是同一个,所以两个线程同步执行(与synchronized(MyObject.class){ }一个效果),运行结果以下
Thread-0====init begin Thread-0====init end Thread-1====init begin Thread-1====init end
定义操做类MyObject
package com.feng.example; public class MyObject { synchronized static public void init() { try { System.out.println(Thread.currentThread().getName() + "====init begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "====init end"); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } synchronized public void print() { try { System.out.println(Thread.currentThread().getName() + "====print begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "====print end"); } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
定义两个线程类:
package com.feng.example; public class MyThreadA extends Thread{ private MyObject object; public MyThreadA(MyObject object) { this.object = object; } @Override public void run() { object.init(); } }
package com.feng.example; public class MyThreadB extends Thread{ private MyObject object; public MyThreadB(MyObject object) { this.object = object; } @Override public void run() { object.print(); } }
测试类以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThreadA(object); Thread b = new MyThreadB(object); a.start(); b.start(); } }
运行结果以下:
Thread-1====print begin Thread-0====init begin Thread-1====print end Thread-0====init end
从运行结果能够看出,class锁与对象锁不是同一个锁,二者异步执行
调用多个synchronized方法的脏读问题
若是在程序中连续调用几个synchronized方法,其中存在分支判断就会出现脏读的问题,下面举例说明:
定义一个只能存一个数据的对象MyObject,判断当数据的个数<1时添加元素,其中获取元素个数,添加元素都为synchronized方法。
定义操做类:
package com.feng.example; import java.util.ArrayList; import java.util.List; public class MyObject { private List<String> list = new ArrayList<String>(); synchronized public void add() { list.add("xu"); } synchronized public int size() { return list.size(); } }
定义线程类:
package com.feng.example; public class MyThread extends Thread { private MyObject object; public MyThread(MyObject object) { this.object = object; } @Override public void run() { try { //保证object中的list中只有一个数据 if(object.size() < 1) { Thread.sleep(2000); object.add(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
测试类以下:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThread(object); Thread b = new MyThread(object); a.start(); b.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(object.size()); } }
分析:两个线程a,b在执行时,线程a执行完size( )执行add()方法前释放锁,此时,线程b获取锁执行了size()方法,两个线程同时进入了if(object.size()<1)的语句块,两个线程均可以往object对象中添加数据,因此添加了两条数据,然而咱们定义的确是只能保存一个数据的对象。所以程序出现了问题。
程序运行结果以下:
2
修改程序将线程类的run方法中使用synchronized语句块。
修改线程类:
package com.feng.example; public class MyThread extends Thread { private MyObject object; public MyThread(MyObject object) { this.object = object; } @Override public void run() { try { synchronized(object) { //保证object中的list中只有一个数据 if(object.size() < 1) { Thread.sleep(2000); object.add(); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
程序运行结果:
1
在程序的运行过程当中,锁对象有可能会发生变化,当锁对象发生变化时会出现什么状况呢?
定义操做类:
package com.feng.example; public class MyObject { private String str = "hahah"; public void init() { try { synchronized(str) { System.out.println(Thread.currentThread().getName() + "====init begin"); str = "hehhe"; //锁对象发生了变化 Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
定义线程类:
package com.feng.example; public class MyThread extends Thread { private MyObject object; public MyThread(MyObject object) { this.object = object; } @Override public void run() { object.init(); } }
定义测试类:
package com.feng.example; public class ThreadTest { /** * @param args */ public static void main(String[] args) { MyObject object = new MyObject(); Thread a = new MyThread(object); Thread b = new MyThread(object); a.start(); b.start(); } }
分析:线程a,b执行同一个对象的object.init( )方法,二者谁先执行到synchronized(str)是不肯定的,假设线程a先到,那么a获取到str对象锁,执行synchronized语句块,接着修改了锁对象str = "hehhe",此时线程b执行到语句块synchronized(str),这里str执行的是hehhe了,不在是hahah,二者不是同一个锁,所以会异步执行
执行结果:
Thread-1====init begin Thread-0====init begin Thread-1====init end Thread-0====init end
修改操做类MyObject对象的代码:
package com.feng.example; public class MyObject { private String str = "hahah"; public void init() { try { synchronized(str) { System.out.println(Thread.currentThread().getName() + "====init begin"); Thread.sleep(2000); str = "hehhe"; //锁对象发生了变化 System.out.println(Thread.currentThread().getName() + "====init end"); } } catch (InterruptedException e) { System.out.println("抛出异常"); // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:线程a,b执行同一个对象的object.init( )方法,二者谁先执行到synchronized(str)是不肯定的,假设线程a先到,线程a获取了str的锁,而后休眠2s,在执行str="hehhe"以前,线程b执行到synchronized(str),二者抢的是同一个对象的锁,所以会同步执行。
运行结果以下;
Thread-0====init begin Thread-0====init end Thread-1====init begin Thread-1====init end
总结:当线程运行到synchronized语句块是,锁对象是哪个对象,就会保持哪个对象的锁。