​锁类型讲解

锁类型讲解

一、锁导图

对于锁的分类,需要从不同角度进行考察,这些分类并不是互斥的,也就是多个类型可以并存;有可能一个锁同时属于两种类型。比如ReentrantLock既是互斥锁,又是可重入锁

二、乐观锁和悲观锁

1、为什么会诞生非互斥同步锁(乐观锁)

互斥同步锁(悲观锁)的劣势:

1)阻塞和唤醒带来的性能劣势;2)永久阻塞:如果持有锁的线程被永久阻塞,比如遇到了无限循环、死锁等活跃性问题,那么等待该线程释放锁的那几个悲催的线程,将永远也得不到执行;3)优先级反转:就是设置的优先级别毫无用处。

java中悲观锁的实现就是synchronizedLock相关类。

2、乐观锁介绍

1)认为自己在处理操作的时候,不会有其他线程来干扰,所以并不会锁住被操作对象;

2)在更新的时候,去对比在我修改的期间数据有没有被其他人改变过;如果没被改变过,就说明真的是只有自己在操作,那就正常去修改数据;

3)如果数据和我一开始拿到的不一样了,说明其他人在这段时间内改过数据,那就不能继续刚才的更新数据过程,我会选择放弃、报错、重试等策略;

4)乐观锁的实现一般都是利用CAS算法来实现的。

乐观锁的典型例子就是原子类并发容器等。

3、开销对比

1)悲观锁的原始开销要高于乐观锁,但是特点是一劳永逸,临界区持锁时间就算越来越差,也不会对互斥锁的开销造成影响。

2)相反,虽然乐观锁一开始的开销比悲观锁小,但是如果自旋时间很长或者不停重试,那么消耗的资源也会越来越多

4、两种锁各自的使用场景

1)悲观锁:适合并发写入多的情况,适用于临界区持锁时间比较长的情况,悲观锁可以避免大量的无用自旋等消耗,典型情况:

a.临界区有IO操作;b.临界区代码复杂或者循环量大;c.临界区竞争非常激烈

2)乐观锁:适合并发写入少,大部分是读取的场景,不加锁的能让读取性能大幅提高。

三、可重入锁和非可重入锁

1、什么是可重入?

可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。synchronizedReentrantLock都是可重入锁。

2、好处

1)避免死锁;2)提升封装性;

3、可重入锁简单测试

public class GetHoldCount { private static ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { System.out.println(lock.getHoldCount()); lock.lock(); System.out.println(lock.getHoldCount()); lock.lock(); System.out.println(lock.getHoldCount()); lock.lock(); System.out.println(lock.getHoldCount()); lock.unlock(); System.out.println(lock.getHoldCount()); lock.unlock(); System.out.println(lock.getHoldCount()); lock.unlock(); System.out.println(lock.getHoldCount()); } }

运行结果

0

1

2

3

2

1

0

4、源码层面进行对比

四、公平锁和非公平锁

1、什么是公平和非公平

1)公平指的是按照线程请求的顺序,来分配锁;非公平指的是,不完全按照请求的顺序,在一定情况下,可以插队。

2、为什么要有非公平锁

1)为了提高效率;2)避免唤醒带来的空档期

ReentrantLock lock = new ReentrantLock(true); // 公平锁

ReentrantLock lock = new ReentrantLock(false); // 非公平锁

3、对比公平和非公平的优缺点

优势

劣势

公平锁

各线程公平平等,每个线程在等待一段时间后,总有执行的机会

更慢、吞吐量更小

不公平锁

更快,吞吐量更大

有可能产生线程饥饿,也就是某些线程在长时间内,始终得不到执行。

4、源码分析

五、共享锁和排他锁

以ReentrantReadWriteLock读写锁为例

1、什么是共享锁和排它锁

1)排它锁,又称为独占锁、独享锁;

2)共享锁,又称为读锁,获得共享锁之后,可以查看,但无法修改和删除数据,其他线程此时也可以获取到共享锁,也可以查看但无法修改和删除数据。

3)共享锁和排它锁的典型是读写锁ReentrantReadWriteLock,其中读锁是共享锁,写锁是独享锁。

2、读写规则

1)多个线程只申请读锁,都可以申请到;

2)如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。

3)如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。

4)总结:两者不会同时出现(要么多读,要么一写)。

3、代码示例

public class ReadWriteTest { private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); private static void read() { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + "得到了读锁,正在读取"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName() + "释放了读锁"); readLock.unlock(); } } private static void write() { writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + "得到了写锁,正在写入"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName() + "释放了写锁"); writeLock.unlock(); } } public static void main(String[] args) { new Thread(() -> read(), "Thread1").start(); new Thread(() -> read(), "Thread2").start(); new Thread(() -> write(), "Thread3").start(); new Thread(() -> write(), "Thread4").start(); } }

运行结果:

4、读锁插队策略

1)公平锁:不允许插队 2)非公平锁:a:写锁可以随时插队 b:读锁仅在等待队列头节点不是想获得写锁的线程的时候可以插队。

5、读写锁,只能降级,不能升级

降级可以提高效率,升级会导致阻塞

6、适用场景

ReentrantReadWriteLock适用于读多写少的情况,合理使用可以进一步提高并发效率。

六、自旋锁和阻塞锁

1、自旋锁理解

1)阻塞或唤醒一个java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。

2)如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长

3)在许多场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。

4)如果物理机有多个处理器,能够让两个或以上的线程同事并行执行,我们就可以让后面那个请求锁的线程不放弃cpu的执行时间,看看持有锁的线程是否很快就会释放锁。

5)而为了让当前线程"稍等一下",我们需让当前线程进行自旋,如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销,这就是自旋锁。

6)阻塞锁和自旋锁相反,阻塞锁如果遇到没拿到锁的情况,会直接把线程阻塞,直到被唤醒。

2、原理

AtomicInteger的实现:自旋锁的实现原理是CAS。

AtomicInteger中调用unsafe进行自增操作的源码中do-while循环就是一个自旋操作,如果修改过程中遇到其他线程竞争导致没修改成功,就在while里死循环,直到修改成功。

3、代码示例

public class SpinLock { private  AtomicReference

sign =  new AtomicReference<>(); public void lock() { Thread current = Thread.currentThread(); while (!sign.compareAndSet(null, current)) { System.out.println(Thread.currentThread().getName() + "自旋获取失败"); } } public void unlock() { Thread current = Thread.currentThread(); sign.compareAndSet(current, null); } public static void main(String[] args) { SpinLock spinLock = new SpinLock(); Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始尝试获取自旋锁"); spinLock.lock(); System.out.println(Thread.currentThread().getName() + "获取到了自旋锁"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLock.unlock(); System.out.println(Thread.currentThread().getName() + "释放了自旋锁"); } } }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); thread2.start(); } }

运行结果:

4、自旋锁的适用场景

1)自旋锁一般用于多核的服务器,在并发度不是特别高的情况下,比阻塞锁的效率高。

2)另外,自旋锁适用于临界区比较短小的情况,否则如果临界区很大(线程一旦拿到锁,很久以后才会释放),那也是不适合的。

同类推荐

​每日一星——赵保乐

​每日一星——赵保乐

每日一星——赵保乐 赵保乐,1958年出生于山东青岛,相声演员、主持人。 1987年,出演电视小品《开心阿Q》。1988年加入中国广播艺术团,正式成为中国广播艺术...

​辽宁队最强名单

​辽宁队最强名单

辽宁队最强名单 目前辽宁男篮的12人名单是郭艾伦、赵继伟、鄢手骐、张镇麟、付豪、丛明晨、赵率舟、李晓旭、韩德君、弗格、莫兰德、亚当斯,这12名球员组成...

​53%vol7.5L贵州茅台酒(元青花)

​53%vol7.5L贵州茅台酒(元青花)

53%vol7.5L贵州茅台酒(元青花) 元青花茅台酒是根据元代青花龙坟梅瓶式样构思设计而成的文创产品。元代青花龙纹梅瓶起初是元代垦室御用盛酒礼器、瓶身绘制的...

​15秒记一个单词(第2872个)space

​15秒记一个单词(第2872个)space

15秒记一个单词(第2872个)space space /speɪs/ n. 空间,空地方: 点击音频收听跟读 ↓↓↓↓↓↓(网络问题不显示请关闭头条app后台重新打开) 例句/词组: Theres sp...

​现言 || 甜宠文第二波,热乎乎的爱情呀

​现言 || 甜宠文第二波,热乎乎的爱情呀

现言 || 甜宠文第二波,热乎乎的爱情呀 Hello,Hello,我亲爱的小伙伴们,小螃蟹又来了。 今天给大家推荐的还是小甜文啊,甜文是主旋律啊。 1.《小清欢》 *男女主双...

​愤怒的小鸟丨超高清手机壁纸

​愤怒的小鸟丨超高清手机壁纸

愤怒的小鸟丨超高清手机壁纸 你的青春就是一场远行, 一场离自己的童年, 离自己的少年, 越来越远的远行, 你会发现这个世界跟你想象的一点都不一样, 你...

​德惠市概况

​德惠市概况

德惠市概况 德惠市是长春市直属县级市,位于长春市的东北部,北及东北隔松花江与松原市、榆树市和舒兰市相望,东南与九台区相连,西隔伊通河与农安县毗邻...

​“橘生淮南则为橘,橘生淮北则为枳”

​“橘生淮南则为橘,橘生淮北则为枳”

“橘生淮南则为橘,橘生淮北则为枳” ——《迁徙与家园》系列之三 1 东北人把山东习惯称之为“关里家”,回山东就是回关里。山海关作为矗立在辽西走廊山海...

​阿斯伯格患者苏珊大妈的47年忍辱逆袭路

​阿斯伯格患者苏珊大妈的47年忍辱逆袭路

阿斯伯格患者苏珊大妈的47年忍辱逆袭路 阅读前请点击“关注”,每天都会定时发送自闭症的相关文章,让我们一起关怀来自星星的孩子~伴他们成长唷~ 阿斯伯...

​北京市行政区划最佳方案

​北京市行政区划最佳方案

北京市行政区划最佳方案 北京市,总面积约1.7万平方千米,人口约2189.31万人。行政区划单位为16个区,分别是东城、西城、朝阳、海淀、丰台、石景山、大兴、通...

热门排行

随机推荐