synchronized 关键字简洁、清晰、语义明确,因此即使有了 Lock 接口,使用的还是非常广泛。其应用层的语义是可以把任何一个非 null 对象作为”锁”。
synchronized 在软件层面依赖 JVM,Lock 在硬件层面依赖特殊的 CPU 指令。
JVM 如何实现 synchronized
在 java 语言中存在两种内建的 synchronized 语法:synchronized 语句和 synchronized 方法。
synchronized 语句被 javac 编译成 bytecode 时,会在同步块的入口位置和退出位置分别插入 monitorenter 和 monitorexit 字节码指令。
synchronized 方法被 javac 编译成 bytecode 时,会被翻译成普通的方法调用和返回指令如:invokevirtual、areturn 指令,在 VM 字节码层面并没有任何特别的指令来实现被 synchronized 修饰的方法,而是在 Class 文件的方法表中将该方法的 access_flags 字段中的 synchronized 标志位置 1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的 Class 在 JVM 的内部对象表示 Klass 做为锁对象。
hotspot 当前对 synchronized 的实现
当前的 hotspot 共有 3 种类型的锁,来实现 synchronize 的语义,之所以有 3 种,是因为这 3 种要解决的问题不同,所做的优化也不同。这 3 种锁分别为 biased locking,stack lock,infalted(ObjectMonitor).简单除暴的来讲,从轻量级上来说,biased lock 最优,inflated 最差。
synchronized 锁住的是什么
synchronized 锁定的是对象而非函数或代码。
当 synchronized 作用在方法上时,锁住的便是对象实例(this);当作用在静态方法时锁住的便是对象对应的 Class 实例,因为 Class 数据存在于永久带,因此静态方法锁相当于该类的一个全局锁;当 synchronized 作用于某一个对象实例时,锁住的便是对应的代码块。
每个对象只有一把锁(Lock)与之关联,当进行到 synchronized 语句或函数的时候,这把锁就会被当前的线程(thread)拿走,其他的(thread)再去访问的时候拿不到锁就被暂停了。
在 HotSpot JVM 实现中,锁有个专门的名字:对象监视器。
synchronized 的使用场景
- public synchronized void method1
锁住的是该对象,类的其中一个实例,当该对象(仅仅是这一个对象)在不同线程中执行这个同步方法时,线程之间会形成互斥。达到同步效果,但如果不同线程同时对该类的不同对象执行这个同步方法时,则线程之间不会形成互斥,因为他们拥有的是不同的锁。 - synchronized(this){ //TODO }
同一 - public synchronized static void method3
锁住的是该类,当所有该类的对象(多个对象)在不同线程中调用这个 static 同步方法时,线程之间会形成互斥,达到同步效果,但如果多个线程同时调用 method1,method3,则不会引互斥,具体讲看最后讲解。 - synchronized(Test.class){ //TODO}
同三 - synchronized(o) {}
这里面的 o 可以是一个任何 Object 对象或数组,并不一定是它本身对象或者类,谁拥有 o 这个锁,谁就能够操作该块程序代码。
参考资料
- https://blogs.oracle.com/dave/entry/biased_locking_in_hotspot
- http://www.javaworld.com/article/2076971/java-concurrency/how-the-java-virtual-machine-performs-thread-synchronization.html
- http://f.dataguru.cn/thread-472518-1-1.html