为什么JDK 15要废弃偏向锁?
要想说清楚这个问题,你得先知道什么是偏向锁,它是在哪里使用的。
这就不得不提到Synchronized的锁升级过程了。
在JDK 1.6及之前的版本中,Synchronized关键字,它可以让一个对象只能被一个线程使用,这样就可以避免多个线程同时修改同一个对象造成的混乱。
但是,这种方式也有一个缺点,就是每次一个线程要使用一个对象时,都要先检查这个对象是否被别的线程占用了,如果是的话,就要等待别的线程用完才能继续。
这个过程会消耗很多时间和资源,影响程序的效率。
为了解决这个问题,Java在后来的1.7 之后对 Synchronized 的实现做了一些改进,让它可以根据不同的情况,采用不同的方式来控制对象的使用。
具体来说,有四种方式:
无锁状态:这是最简单的一种方式,就是没有任何限制,任何线程都可以随时使用对象。
偏向锁状态:这是一种优化的方式,就是假设一个对象只会被一个线程使用,所以当第一个线程使用对象时,就会把自己的标记放在对象上,表示这个对象属于自己。这样,下次这个线程再使用对象时,就不用检查了,直接就可以用。但是如果有别的线程也想用这个对象,就要先把标记去掉,然后再竞争。
轻量级锁状态:这是一种折中的方式,就是当有多个线程想用同一个对象时,不会立刻让它们等待,而是先让它们在自己的内存里复制一份对象的信息,然后各自修改。最后再比较一下谁修改得最快,谁就可以用对象。这样可以减少等待的时间,提高效率。
重量级锁状态:这是最原始的一种方式,就是当有多个线程想用同一个对象时,只能让其中一个线程用,其他的线程都要等待。这样可以保证对象的安全性,但是会降低效率。
在Java中,每个对象都有一个特殊的区域叫做mark word(标记字),它可以记录对象的一些信息。其中有两位或三位是用来表示对象现在处于哪种状态的。具体来说:
如果两位是“01”,表示无锁状态或偏向锁状态。如果第三位是“0”,表示无锁状态;
如果第三位是“1”,表示偏向锁状态。如果两位是“00”,表示轻量级锁状态。
如果两位是“10”,表示重量级锁状态。
但是,在Java 15中 ,偏向锁被取消了 ,所以现在只有三种状态:无锁状态、轻量级锁状态和重量级锁状态。
回到最开始的问题:为什么JDK 15要废弃偏向锁?
取消偏向锁状态的原因是,偏向锁状态在现代的应用场景下已经不再有明显的性能优势,反而会增加虚拟机的复杂度和维护成本。
具体的原因是:
1. 偏向锁状态是一种优化技术,用于减少无竞争情况下的同步开销。
它假设一个对象只会被一个线程使用,所以当第一个线程使用对象时,就会把自己的标记放在对象上,表示这个对象属于自己。
这样,下次这个线程再使用对象时,就不用检查了,直接就可以用。
但是如果有别的线程也想用这个对象,就要先把标记去掉,然后再竞争。
2. 偏向锁状态在过去能够带来很大的性能提升,是因为有些应用会使用很多不必要的同步操作。
比如早期的Java集合类(如Hashtable和Vector),它们每次访问都要同步。
现在的应用一般会使用非同步的集合类(如HashMap和ArrayList)或者更高效的并发数据结构(如ConcurrentHashMap、CopyOnWriteArrayList等)来处理单线程或多线程的情况。
这意味着如果更新代码使用这些新的类,就可以获得更好的性能,而不需要依赖偏向锁状态。
3. 偏向锁状态也有一个缺点,就是当有竞争发生时,需要进行一次代价很高的撤销操作。
所以只有当应用有大量的无竞争的同步操作时,偏向锁状态才有优势。
但是随着硬件和软件的发展,原子操作的代价已经降低了很多,而且现在的应用往往使用线程池和工作队列来处理并发任务,这些场景下偏向锁状态反而会降低性能。
4. 偏向锁状态还给虚拟机带来了很多复杂度和维护难度,只有少数几个最有经验的工程师能够理解和修改它。它也影响了一些新特性的设计和实现。
综上所述,偏向锁状态已经不再适合现代的Java应用,所以在Java 15 之后被废弃。
暂无评论内容