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)获得的是全局锁,两把不同的锁,所以不能达到同步效果。