java锁机制

来源:互联网 发布:山西省网络研修社区 编辑:程序博客网 时间:2024/05/16 17:34
1.java锁种类及相关概念
1、自旋锁
2、自旋锁的其他种类
3、阻塞锁
4、可重入锁
5、读写锁
6、互斥锁
7、悲观锁
8、乐观锁
9、公平锁
10、非公平锁
11、偏向锁
12、对象锁
13、线程锁
14、锁粗化
15、轻量级锁
16、锁消除
17、锁膨胀

18、信号量

2.锁分类详解

2.1 自旋锁

 其中,自旋锁,是指线程在等待一个锁的时候,不放弃cpu时间,而是在忙等。这个功能是有缺点的,比如说,等待时间过长,那么自旋只会白白浪费cpu时间。如果等待时间很短,那么自旋倒是很有价值。

2.2 自旋锁的其他种类

在自旋锁中 另有三种常见的锁形式:TicketLock ,CLHlock 和MCSlock

Ticket锁主要解决的是访问顺序的问题,主要的问题是在多核cpu上。每次都要查询一个serviceNum 服务号,影响性能(必须要到主内存读取,并阻止其他cpu修改)。

CLHLock 和MCSLock 则是两种类型相似的公平锁,采用链表的形式进行排序,CLHlock是不停的查询前驱变量, 导致不适合在NUMA 架构下使用(在这种结构下,每个线程分布在不同的物理内存区域)。MCSLock则是对本地变量的节点进行循环。不存在CLHlock 的问题。

从代码上 看,CLH 要比 MCS 更简单,

CLH 的队列是隐式的队列,没有真实的后继结点属性。

MCS 的队列是显式的队列,有真实的后继结点属性。

2.3 阻塞锁

阻塞锁,与自旋锁不同,改变了线程的运行状态。

阻塞锁,可以说是让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间) 时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程,通过竞争,进入运行状态。

JAVA中,能够进入\退出、阻塞状态或包含阻塞锁的方法有 ,synchronized 关键字(其中的重量锁),ReentrantLock,Object.wait()\notify(),LockSupport.park()/unpart()(j.u.c经常使用)

阻塞锁的优势在于,阻塞的线程不会占用cpu时间, 不会导致 CPu占用率过高,但进入时间以及恢复时间都要比自旋锁略慢。

在竞争激烈的情况下 阻塞锁的性能要明显高于 自旋锁。

理想的情况则是; 在线程竞争不激烈的情况下,使用自旋锁,竞争激烈的情况下使用,阻塞锁。

2.4 重入锁

Java中的synchronized同步块是可重入的。这意味着如果一个java线程进入了代码中的synchronized同步块,并因此获得了该同步块使用的同步对象对应的管程上的锁,那么这个线程可以进入由同一个管程对象所同步的另一个java代码块。

如果一个线程已经拥有了一个管程对象上的锁,那么它就有权访问被这个管程对象同步的所有代码块。这就是可重入。线程可以进入任何一个它已经拥有的锁所同步着的代码块。

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