synchronized锁住了什么

先看一个简单示例,下面这段代码能够正常通过编译。

public  class  SyncTest {

     public  SyncTest  syncVar ;

     public  static  SyncTest  syncStaticVar ;

     public  static  synchronized  void  testStaticSync() {

    }

     public  synchronized  void  testNonStaticSync() {

    }

     public  void  testSyncThis() {
         synchronized  ( this  ) {
             try  {
                System.  out .println( "test sync this start"  );
                Thread. sleep(5000);
                System.  out .println( "test sync this end"  );
            }  catch  (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

     public  void  testSyncVar() {
         synchronized  ( syncVar  ) {
             try  {
                System.  out .println( "test sync var start"  );
                Thread. sleep(3000);
                System.  out .println( "test sync var end"  );
            }  catch  (InterruptedException e) {

            }
        }
    }

     public  void  testStaticSyncVar() {
         synchronized  ( syncStaticVar  ) {

        }
    }

     public  static  void  main(String[] args) {
         final  SyncTest testSync =  new  SyncTest();
        testSync.  syncVar  =  new  SyncTest();
        testSync.  syncVar  = testSync;
        Thread threadOne =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                testSync.testSyncThis();
            }
        });
        Thread threadTwo =  new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                testSync.testSyncVar();
            }
        });
        threadOne.start();
        threadTwo.start();
    }
}

从上面的代码来看,synchronized使用的场景很广,既能够锁住类方法(static),又能够锁住对象方法(非static)。既能够对某个属性加锁,又能够对this加锁。那么,sychronized到底锁住了什么?另外,synchronized也是可重入的,那么,它与单独的ReentrantLock的区别是什么?

sychronized在使用的时候也是区分目标对象的,在这一点上其实非常像前面提到过的ReentrantLock的使用。上面的代码中,如果把 testSync.  syncVar  = testSync;这句话注释掉,那么最后运行所产生的结果就会不一样。如果要和ReentrantLock做对比的话,可以把synchronized所针对的目标对象理解成一个锁,针对的目标对象不一样,那么就是不同的锁。


sychronized的实现原理需要深入到jvm中才能找到答案。。可以参考《深入理解Java虚拟机》

在这里主要讨论一个使用上的问题,当我们使用sychronized锁住某个对象时,我们锁住的是这个引用本身,还是内存(堆)中的这个对象本身。对这个问题的一个延伸是,当我们在sychronized作用区域内,为这个引用附一个新值的时候,sychronized是否还有效?

先给出结论,sychronized锁住的是内存(堆)中的对象,当引用被附上新值的时候,则相当于旧对象的锁被释放。这里不做理论讨论,只是用程序进行验证。

用三个例子进行说明,

示例1、
public   class  TestSyncAndModify  implements  Runnable {

     private  A  syncA ;

     @Override
     public   void  run() {
         synchronized  ( syncA  ) {
            System.  out  .println(Thread.currentThread().getName());
             syncA = new A();
             try  {
                Thread. sleep(3000);
            }  catch  (InterruptedException e) {
                e.printStackTrace();
            }
            System.  out  .println(Thread.currentThread().getName());
        }
    }

     static   class  A {
    };

     public   static   void  main(String[] args) {
        TestSyncAndModify sync =  new  TestSyncAndModify();
        A testA =  new  A();
        sync.  syncA  = testA;
        Thread one =  new  Thread(sync);
        Thread two =  new  Thread(sync);
        one.start();
         try  {
            Thread. sleep(1000);
        }  catch  (InterruptedException e) {
            e.printStackTrace();
        }
        two.start();
    }
}

在sychronized作用域内对syncA做了修改,使它指向了一个新的对象,所以当这句话执行完之后,第二个线程就可以运行,因此输出的结果如下

Thread-0
Thread-1
Thread-0
Thread-1



示例2、就是最上面的那段代码SyncTest。在main函数内,有这样一句赋值 testSync.  syncVar  = testSync;使得syncVar成员变量,指向了和this相同的区域。因此,在sychronized(this)和synchronized(syncVar)就形成了竞争,使得后者被阻塞,因此输出结果如下
test sync this start
test sync this end
test sync var start
test sync var end

示例3
public   class  ThreadUseBase  extends  Thread {

     private  Base  baseObject  ;

     public  ThreadUseBase(Base boj) {
         baseObject  = boj;
    }

     public   void  testSyncBase() {
         synchronized  ( baseObject  ) {
            System.  out  .println( "enter thread use base"  );
             try  {
                Thread. sleep(2000);
            }  catch  (InterruptedException e) {
                e.printStackTrace();
            }
            System.  out  .println( "leave thread use base"  );
        }
    }

     @Override
     public   void  run() {
        testSyncBase();
    }

     static   class  ThreadUseChild  extends  Thread {
         private  SyncObject  childObj  ;

         public  ThreadUseChild(SyncObject sobj) {
             childObj  = sobj;
        }

         public   void  testSyncChild() {
             synchronized  ( childObj  ) {
                System.  out  .println( "enter thread use child"  );
                 try  {
                    Thread. sleep(2000);
                }  catch  (InterruptedException e) {
                    e.printStackTrace();
                }
                System.  out  .println( "leave thread use child"  );
            }
        }

         @Override
         public   void  run() {
            testSyncChild();
        }
    }

     static   class  Base {

    }

     static   class  SyncObject  extends  Base {

    }

     public   static   void  main(String[] args) {
        SyncObject childObj =  new  SyncObject();
         Base baseObj = childObj;
        //Base baseObj =  new  Base();
        ThreadUseBase threadBase =  new  ThreadUseBase(baseObj);
        ThreadUseChild threadChild =  new  ThreadUseChild(childObj);
        threadBase.start();
        threadChild.start();
    }

}

虽然是两个线程中,sychronized锁住的是不同的引用,一个是Base一个是SyncObject,但由于存在继承关系,在main函数中,我们让Base对象也指向了SyncObject内存区域。这样,就形成了两个不同线程之间的竞争,因此后者被阻塞,输出结果如下
enter thread use base
leave thread use base
enter thread use child
leave thread use child

参考论文如下
JVM的锁结构设计原理 ---  https://www.usenix.org/legacy/event/jvm01/full_papers/dice/dice.pdf
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值