java中的锁---锁的相关概念

java中的锁—锁的相关概念

常见的锁:
1,死锁活锁
2,悲观锁,乐观锁
3,独享锁/共享锁/互斥锁/读写锁
4,公平锁/非公平锁
5,可重入锁/不可重入锁
6,偏向锁/轻量级锁/重量级锁/自旋锁
7,分段锁

1,死锁和活锁
https://blog.csdn.net/benbenxiongyuan/article/details/8960611
死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
死锁成立的必要条件成立四个条件
1)互斥条件:某一个资源只能被一个线程占用,没有得到资源的线程只能等待
2)请求和保持条件:线程已经至少占用了一个资源,又开始请求新的资源,在请求不到的情况下,不释放自己已经占用的资源
3)不剥夺条件:自己占用的资源只能自己释放,别人不能剥夺
4)环路等待条件:A得到B占用的资源后才能下一步操作,B得到A占用的资源进行下一步操作
避免死锁:
1)资源有序分配法:所有的资源必须按照一定的顺序申请(破坏环路等待)
2)银行家算法:申请者检查对资源的最大需求量,如果满足所有需求,则请求通过

活锁:线程A可以使用某一资源,但他会先让给线程B使用,线程B也一样,然后两者开始相互谦让,都无法使用资源
饥饿:是指如果事务T1封锁了数据R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁后,系统首先批准了T3的请求,T2仍然等待。
然后T4又请求封锁R,当T3释放了R上的封锁之后,系统又批准了T4的请求......T2可能永远等待,这就是饥饿

2,悲观锁,乐观锁
悲观锁: 先加锁,再操作,认为每次操作数据的时候,都会发生同步问题,从而提前对数据进行加锁,然后进行数据操作,
而加锁释放锁的过程会造成消耗,所以

		性能不高,java中synchronized和ReentrantLock就是悲观锁的一种实现,
乐观锁: 乐观的认为每次数据操作不会导致冲突,所以不加锁,在进行数据更新时再去判断是否有冲突.
		在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
乐观锁的实现: 
1,版本号机制,每次数据被修改时,都会更新版本号,在提交更新的时候按段,版本号是否一致,如果一致才提交,否则重试操作
2,CAS算法(compare and swap)实现https://www.jianshu.com/p/8de8c6a839e8: 
	CAS有三个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则则重新执行读取流程.
	CAS存在三大问题。ABA问题,循环时间长开销大,只能保证一个共享变量的原子操作。
		1).ABA问题。因为CAS需要在操作值得时候检查值有没有发生变化。如果没有发生变化则更新。
			但是一个值A变为B,又变成A,那么CAS进行检查的时候并没有发现它的变化,但实际已经发生了变化。
			ABA问题解决的思路是时候版本号。在变量前面加上版本号。每次变量的更新都把版本号加一。A-B-A会变成1A-2B-3A。
		2).循环时间常开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
			如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),
			使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。
			第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),
			从而提高CPU的执行效率。
		3).只能保证一个共享变量的院子操作。当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,
			但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,
			就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。
			从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

3,独享锁/共享锁/互斥锁/读写锁
独享锁:独享锁每一次只能被一个线程所持有。
共享锁:共享锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁.
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
互斥锁:顾名思义,在访问共享文件的之前都会加锁,在访问完成之后进行解锁操作,加锁之后其他试图再次加锁的线程会陷入阻塞,直到当前线程解锁
读写锁:读写锁在Java中的具体实现就是ReadWriteLock,读加锁状态是共享锁.可被多个线程持有,写加锁状态是互斥锁

4,公平锁/非公平锁
公平锁:是指多个线程按照申请锁的顺序来获取锁。
非公平锁:非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。

5,可重入锁/不可重入锁
可重入锁:可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提是同一个锁,且获得锁的次数与释放锁的次数是对应的)
不可重入锁: 相反
常见的synchronized、Lock均为可重入锁

6,偏向锁/轻量级锁/重量级锁/自旋锁,比较繁多,不知道该怎么写
参考:https://developer.aliyun.com/article/574663
Java SE1.6为了减少获得锁和释放锁所带来的性能消耗(是针对Synchronized),引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,
它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

7,分段锁
在jdk1.7中ConcurrentHashMap的底层数据结构是数组加链表。(jdk 1.8中ConcurrentHashmap采用的底层数据结构为数组+链表+红黑树的形式)
ConcurrentHashMap中存放的数据是一段段的,即由多个Segment(段)组成的。每个Segment中都有着类似于数组加链表的结构。
分段锁的优势在于保证在操作不同段 map 的时候可以并发执行,操作同段 map 的时候,进行锁的竞争和等待。这相对于直接对整个map同步synchronized是有优势的。
缺点在于分成很多段时会比较浪费内存空间(不连续,碎片化); 操作map时竞争同一个分段锁的概率非常小时,分段锁反而会造成更新等操作的长时间等待; 当某个段很大时,分段锁的性能会下降。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值