多线程同步机制对资源进行锁定,使得同一时间只能有一个线程进行操作,同步用于解决多个线程同时访问时可能出现的问题。可以使用synchronized关键字来实现同步机制。当synchronized关键字修饰一个方法时,该方法被称为同步方法。当synchronized方法执行或异常发生时,锁会自动释放。下面通过一个例子来分析一下synchronized关键字的用法。1.不同的publicclassThreadTest{publicstaticvoidmain(String[]args){Exampleexample=newExample();Threadt1=newThread1(example);Threadt2=newThread1(example);Threadt2=newThread1(example);t1.start();t2.start();}}classExample{publicsynchronizedvoidexecute(){for(inti=0;i<10;++i){try{Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Hello:"+i);}}}classThread1extendsThread{privateExampleexample;publicThread1(Exampleexample){this.example=example;}@Overridepublicvoidrun(){example.execute();}}是否在execute()前添加method加上synchronized关键字,这个例子程序的执行结果就会有很大的不同。如果不加synchronized关键字,两个线程同时执行execute()方法,输出是两组并发。如果加上synchronized关键字,会先输出一组0到9,再输出下一组,说明两个线程是顺序执行的。2.多方法的多线程更改程序,在Example类中增加一个方法execute2()。然后写一个线程类Thread2,Thread2中的run()方法执行execute2()。Example类中的两个方法都由synchronized关键字修饰。publicclassThreadTest{publicstaticvoidmain(String[]args){Exampleexample=newExample();Threadt1=newThread1(例子);Threadt2=newThread2(例子);t1.start();t2.start();}}classExample{publicsynchronizedvoidexecute(){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out。println("Hello:"+i);}}publicsynchronizedvoidexecute2(){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("World:"+i);}}}classThread1extendsThread{privateExampleexample;publicThread1(Exampleexample){this.example=example;}@Overridepublicvoidrun(){example.execute();}}classThread2extendsThread{privateExampleexample;publicThread2(Exampleexample){this.example=example;}@Overridepublicvoidrun(){example.execute2();}}如果去掉synchronized关键字,两个方法都会执行同时。互不影响。但是如示例程序中所写,即使有两种方法:执行结果总是一个线程输出,然后另一个线程输出。解释:如果一个对象有多个synchronized方法,某个线程在某个时刻进入了一个synchronized方法,在该方法执行完之前,其他线程不能访问该对象的任何synchronized方法。结论:当synchronized关键字修饰一个方法时,该方法被称为synchronized方法。Java中的每一个对象都有一个锁(lock),或者说监视器(monitor),当一个线程访问一个对象的synchronized方法时,这个对象就被锁住了,其他线程就不能再访问这个对象的synchronized方法(这里指的是所有synchronizedmethods,notjustthesynchronizedmethods),直到前一个线程执行完该方法(或抛出异常)才释放对象的锁,而其他线程只能访问对象的synchronized方法.请注意,此时对象处于锁定状态。如果是不同的对象,则各个对象之间没有限制关系。在代码中试图构造第二个线程对象时传入一个新的Example对象,两个线程的执行之间没有任何约束。3.考虑静态同步方式。当一个被synchronized关键字修饰的方法也被static修饰时,前面说过,非static同步方法会对对象加锁,而静态方法不属于对象,属于类,就会加锁该方法所在类的Class对象。不管一个类产生多少个对象,它们都对应同一个Class对象。publicclassThreadTest{publicstaticvoidmain(String[]args){Exampleexample=newExample();Threadt1=newThread1(example);//即使这里传入了不同的对象,静态方法同步仍然不允许多个线程同时执行example=newExample();Threadt2=newThread2(例子);t1.start();t2.start();}}classExample{publicsynchronizedstaticvoidexecute(){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Hello:"+i);}}publicsynchronizedstaticvoidexecute2(){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(“世界:”+i);}}}classThread1extendsThread{privateExampleexample;publicThread1(Exampleexample){this.example=example;}@Overridepublicvoidrun(){Example.execute();}}classThread2extendsThread{privateExampleexample;publicThread2(Exampleexample){this.example=example;}@Overridepublicvoidrun(){Example.execute2();}}所以如果是静态方法(execute()和execute2()都加上static关键字),即使将不同的Example对象传递给两个线程,两个线程仍然相互制约,必须先执行一个,然后下一个结论:如果一个synchronized方法是静态的,那么当线程访问该方法时,它锁的不是synchronized方法所在的对象,而是synchronized方法所在的类对应的Class对象。在Java中,无论一个类有多少个对象,这些对象都会对应最后一个Class对象,所以当线程分别访问同一个类的两个对象的两个static和synchronized方法时,它们的执行顺序也是顺序的,即比如说,一个线程先执行该方法,另一个线程在执行完成后才开始执行。4.同步块同步块写法:synchronized(object){}表示线程在执行过程中会锁住object对象。(注意这个对象可以是任何类的对象,也可以使用this关键字)。这样,锁定的对象就可以自己指定了。publicclassThreadTest{publicstaticvoidmain(String[]args){Exampleexample=newExample();Threadt1=newThread1(例子);Threadt2=newThread2(例子);t1.start();t2.start();}}classExample{privateObjectobject=newObject();publicvoidexecute(){synchronized(object){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("Hello:"+i);}}}publicvoidexecute2(){synchronized(object){for(inti=0;i<20;++i){try{Thread.sleep((long)Math.random()*1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("World:"+i);}}}}classThread1extendsThread{privateExampleexample;publicThread1(Exampleexample){this.example=example;}@Overridepublicvoidrun(){example.execute();}}classThread2extendsThread{privateExampleexample;publicThread2(Exampleexample){this.example=example;}@Overridepublicvoidrun(){example.execute2();}}示例程序4实现的效果与示例程序2相同,使得executi两个线程中的一个按顺序而不是同时进行。当一个线程执行时,object对象被锁定,另一个线程无法执行对应的block。synchronized方法其实相当于用一个synchronized块包裹了方法中的所有语句,然后在synchronized块的括号内传入this关键字。.当然,如果是静态方法,就是需要加锁的类对象。可能一个方法中只有几行代码会涉及线程同步问题,所以synchronized块比synchronized方法更细粒度地控制多个线程的访问。只是synchronized块中的内容不能被多个线程同时访问。其他语句仍然可以被多个线程同时访问(包括同步块之前和之后)。注意:由synchronized保护的数据应该是私有的。结论:synchronized方法是一种粗粒度的并发控制。在某一时刻,只有一个线程可以执行synchronized方法;synchronized块是一个细粒度的并发控制,它只同步块内的代码,位于methodsynchronized块内外的其他代码可以被多个线程同时访问。JDK5.0的并发包使用synchronized关键字来解决线程同步问题,会带来一些执行效率问题。JDK1.4及之前版本无法避免这些问题。JDK5.0引入了这样一个包:java.util.concurrent:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-frame.html专门解决了这个问题。限于篇幅,这里不再介绍。
