Java多线程并发编程 Synchronized关键字
synchronized关键字解析
同步锁依赖于对象,每个对象都有一个同步锁。
现有一成员变量Test,当线程A调用Test的synchronized方法,线程A获得Test的同步锁,同时,线程B也去调用Test的synchronized方法,此时线程B无法获得Test的同步锁,必须等待线程A释放Test的同步锁才能获得从而执行对应方法的代码。
综上,正确使用synchronized关键字可确保原子性。
synchronized关键字的特性应用
特性1:
当线程A调用某对象的synchronized方法或者synchronized代码块时,若同步锁未释放,其他线程调用同一对象的synchronized方法或者synchronized代码块时将被阻塞,直至线程A释放该对象的同步锁。
DEMO1,synchronized方法:
publicclassTest{ privatestaticclassCounter{ publicsynchronizedvoidcount(){ for(inti=0;i<6;i++){ System.out.println(Thread.currentThread().getName()+",i="+i); } } } privatestaticclassMyThreadextendsThread{ privateCountermCounter; publicMyThread(Countercounter){ mCounter=counter; } @Override publicvoidrun(){ super.run(); mCounter.count(); } } publicstaticvoidmain(String[]var0){ Countercounter=newCounter(); //注:myThread1和myThread2是调用同一个对象counter MyThreadmyThread1=newMyThread(counter); MyThreadmyThread2=newMyThread(counter); myThread1.start(); myThread2.start(); } }
DEMO1输出:
Thread-0,i=0 Thread-0,i=1 Thread-0,i=2 Thread-0,i=3 Thread-0,i=4 Thread-0,i=5 Thread-1,i=0 Thread-1,i=1 Thread-1,i=2 Thread-1,i=3 Thread-1,i=4 Thread-1,i=5
DEMO2,synchronized代码块:
publicclassTest{ privatestaticclassCounter{ publicvoidcount(){ synchronized(this){ for(inti=0;i<6;i++){ System.out.println(Thread.currentThread().getName()+",i="+i); } } } } privatestaticclassMyThreadextendsThread{ privateCountermCounter; publicMyThread(Countercounter){ mCounter=counter; } @Override publicvoidrun(){ super.run(); mCounter.count(); } } publicstaticvoidmain(String[]var0){ Countercounter=newCounter(); MyThreadmyThread1=newMyThread(counter); MyThreadmyThread2=newMyThread(counter); myThread1.start(); myThread2.start(); } }
DEMO2输出:
Thread-0,i=0 Thread-0,i=1 Thread-0,i=2 Thread-0,i=3 Thread-0,i=4 Thread-0,i=5 Thread-1,i=0 Thread-1,i=1 Thread-1,i=2 Thread-1,i=3 Thread-1,i=4 Thread-1,i=5
可见,当同步锁未释放时,其他线程将被阻塞,直至获得同步锁。
而且DEMO1和DEMO2的输出结果是一样的,synchronized方法和synchronized代码块的不同之处在于synchronized方法作用域较大,作用于整个方法,而synchronized代码块可控制具体的作用域,更精准控制提高效率。(毕竟阻塞的都是时间啊)
DEMO3,仅修改main方法:
publicstaticvoidmain(String[]var0){ //注意:myThread1和myThread2传入的Counter是两个不同的对象 MyThreadmyThread1=newMyThread(newCounter()); MyThreadmyThread2=newMyThread(newCounter()); myThread1.start(); myThread2.start(); }
DEMO3输出:
Thread-0,i=0 Thread-1,i=0 Thread-0,i=1 Thread-1,i=1 Thread-1,i=2 Thread-1,i=3 Thread-0,i=2 Thread-1,i=4 Thread-0,i=3 Thread-1,i=5 Thread-0,i=4 Thread-0,i=5
同步锁基于对象,只要锁的来源一致,即可达到同步的作用。所以,但对象不一样,则不能达到同步效果。
特性2:
当线程A调用某对象的synchronized方法或者synchronized代码块时,若同步锁未释放,其他线程调用同一对象的其他synchronized方法或者synchronized代码块时将被阻塞,直至线程A释放该对象的同步锁。(注意:重点是其他)
DEMO4,仅修改doOtherThings方法的修饰:
publicclassTest{ privatestaticclassCounter{ publicsynchronizedvoidcount(){ System.out.println(Thread.currentThread().getName()+"sleep"); try{ Thread.sleep(3000); }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"awake"); } publicsynchronizedvoiddoOtherThings(){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); } } publicstaticvoidmain(String[]var0){ finalCountercounter=newCounter(); newThread(newRunnable(){ @Override publicvoidrun(){ counter.count(); } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ counter.doOtherThings(); } }).start(); } }
DEMO4输出:
Thread-0sleep Thread-0awake Thread-1doOtherThings
可见,synchronized获得的同步锁并非仅仅锁住代码,而是锁住整个对象。
此时应提及happens-before原则,正因happens-before原则的存在才有此现象的发生。
happens-before原则的其中一条:
管理锁定原则:一个unLock操作先行发生于后面对同一个锁的lock操作。
(此处暂不作过多解释,解释起来能再写一篇文章了)
DEMO5,仅修改doOtherThings方法:
publicvoiddoOtherThings(){ synchronized(this){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); } }
DEMO5输出:
Thread-0sleep Thread-0awake Thread-1doOtherThings
DEMO4和DEMO5的输出结果竟然一致!没错,因为他们的同步锁来源一致(都是本实例自己),所以可以达到同步效果。
//这两个synchronized锁的是同一个对象 publicsynchronizedvoidcount(){}; publicvoiddoOtherThings(){ synchronized(this){} }
DEMO6,去掉doOtherThings方法的同步关键字:
publicvoiddoOtherThings(){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); }
DEMO6输出:
Thread-0sleep Thread-1doOtherThings Thread-0awake
当线程A调用某对象的synchronized方法或者synchronized代码块时,无论同步锁是否释放,其他线程调用同一对象的其他非synchronized方法或者非synchronized代码块时可立即调用。
实例锁和全局锁
以上DEMO实现的都是实例锁。锁住(作用域)的是具体某一对象实例。
什么是全局锁?
锁住整个Class,而非某个对象或实例。
注:单例型的实例锁不属于全局锁。
全局锁的实现:
静态synchronized方法
DEMO7:
publicclassTest{ privatestaticclassCounter{ publicstaticsynchronizedvoidcount(){ System.out.println(Thread.currentThread().getName()+"sleep"); try{ Thread.sleep(3000); }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"awake"); } publicstaticsynchronizedvoiddoOtherThings(){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); } } publicstaticvoidmain(String[]var0){ newThread(newRunnable(){ @Override publicvoidrun(){ Counter.count(); } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ Counter.doOtherThings(); } }).start(); } }
DEMO7输出:
Thread-0sleep Thread-0awake Thread-1doOtherThings
static声明的方法为全局方法,与对象实例化无关,所以staticsynchronized方法为全局同步方法,与对象实例化无关。
synchronized具体Class的代码块
DEMO8:
publicclassTest{ privatestaticclassCounter{ publicstaticsynchronizedvoidcount(){ System.out.println(Thread.currentThread().getName()+"sleep"); try{ Thread.sleep(3000); }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"awake"); } publicvoiddoOtherThings(){ synchronized(Counter.class){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); } } } publicstaticvoidmain(String[]var0){ newThread(newRunnable(){ @Override publicvoidrun(){ Counter.count(); } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ Countercounter=newCounter(); counter.doOtherThings(); } }).start(); } }
DEMO8输出:
Thread-0sleep Thread-0awake Thread-1doOtherThings
synchronized(Counter.class)获得的同步锁是全局的,staticsynchronized获得的同步锁也是全局的,同一个锁,所以达到同步效果。
区分synchronized(this)与synchronized(Class.class)
DEMO9:
publicclassTest{ privatestaticclassCounter{ publicvoidcount(){ synchronized(this){ System.out.println(Thread.currentThread().getName()+"sleep"); try{ Thread.sleep(3000); }catch(InterruptedExceptione){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"awake"); } } publicvoiddoOtherThings(){ synchronized(Counter.class){ System.out.println(Thread.currentThread().getName()+"doOtherThings"); } } } publicstaticvoidmain(String[]var0){ finalCountercounter=newCounter(); newThread(newRunnable(){ @Override publicvoidrun(){ counter.count(); } }).start(); newThread(newRunnable(){ @Override publicvoidrun(){ counter.doOtherThings(); } }).start(); } }
DEMO9输出:
Thread-0sleep Thread-1doOtherThings Thread-0awake
synchronized(this)获得的是具体对象实例counter的锁,而synchronized(Counter.class)获得的是全局锁,两把不同的锁,所以不能达到同步效果。