Java锁机制
问:为什么说java的syncronized关键字的效率很低?这是因为,java中线程是映射到操作系统的原生线程上的。如果要唤醒或者是阻塞一条线程需要操作系统的帮忙。这就需要从用户态转换到核心态。因此,状态转换需要相当长的时间。所以说syncronized关键字是java中比较重量级的操作。虚拟机本身会做一些优化。比如,在通知操作系统阻塞线程之前,加入一段自旋等待过程,避免频繁的切入到和心态中
问:为什么说java的syncronized关键字的效率很低?
这是因为,java中线程是映射到操作系统的原生线程上的。如果要唤醒或者是阻塞一条线程需要操作系统的帮忙。这就需要从用户态转换到核心态。所以状态转换需要相当长的时间。所以说syncronized关键字是java中比较重量级的操作。虚拟机本身会做一些优化。比如,在通知操作系统阻塞线程之前,加入一段自旋等待过程,避免频繁的切入到和心态中。
除了syncronized关键字,java中还可以使用java.lang.concurrent包中的ReentrantLock来实现同步。在基本用法上,二者类似。但是ReentrantLock增加了一些高级特性,主要有以下三个:
第一、等待可中断
第二、公平锁
第三、锁绑定多个条件
等待可中断是指,等待的线程在等待超过一定时间之后,可以选择继续等待,或者也可以不等待直接去做其他事情了。这对于执行时间非常长的同步块来说很有用。
公平锁是指,线程必须按照排队的顺序来获得锁,而非公平锁则不保证这一点。在非公平锁中,任何一个等待的线程都有可能获得锁。
锁绑定多个条件是指,一个ReentrantLock对象可以同时绑定多个Condition对象。而在syncronized中,锁对象的wait和notify或者是notifyall方法可以实现一个隐含的条件,如果要和多于一个的条件进行关联的时候,则不得不额外的添加一个锁。ReentrantLock不需要额外的添加一个锁,只需要多次调用newCondition()方法即可。
与syncronized相比,ReentrantLock的性能可以保持在一个比较稳定的水平。从java1.6开始,syncronized与ReentrantLock在性能上完全持平了。所以在1.6之后,性能就不是选择ReentrantLock的原因了。
---------------------------------------------------------------------------------------
锁的优化
jvm针对锁进行了一些优化
第一、自旋锁和自适应自旋
第二、锁消除
第三、锁粗化
第四、轻量级锁
第五、偏向锁
其中,自旋锁,是指线程在等待一个锁的时候,不放弃cpu时间,而是在忙等。这个功能是有缺点的,比如说,等待时间过长,那么自旋只会白白浪费cpu时间。如果等待时间很短,那么自旋倒是很有价值。
自适应自旋锁于是诞生了。如果上一次自旋成功了,那么自适应自旋锁会认为下一次等待也应该会成功,于是可以适当的放宽自旋的等待时间。如果之前一直自旋失败,那么会省掉这个自旋过程,直接进入线程挂起。
锁消除是指,根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁比如说String a="a"+"b",这段代码中其实是有锁的【想想StringBuffer】,但是编译器把锁给优化掉了。
锁粗化,什么是粗化??比如说String a="a"+"b"+"c",这里其实是有多个锁操作的。但是在同一个函数里面,是没有代码竞争的,根本就没有必要进行锁操作。于是,编译器就可以优化掉这部分锁操作。
轻量级锁,是JDK1.6新增的锁机制。这里的轻量级是针对需要操作系统互斥量来实现的传统锁而言的。因此传统的锁被称作是“重量级锁”。轻量级锁并不是用来取代重量级锁的,他的本意是在没有多线程竞争的情况下,减少传统的重量级锁使用操作系统互斥量产生的性能损耗。
偏向锁,也是1.6才引入的一项锁优化。目的是为了消除数据在无竞争的情况下的同步原语。进一步提高程序的性能。偏向锁就是“偏袒”第一个获得这个锁的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,那么该线程将永远不需要再次进行同步。但是当另外一个线程尝试获取该锁的时候,偏向模式宣告结束。根据锁定对象的当前状态,撤销偏向后恢复到未锁定状态或者是进入轻量级锁定状态。可以看出,如果程序中大多数的锁都是被多个线程所访问的,那么偏向锁就是多余的。
更多推荐
所有评论(0)