JUC---各种锁(JDK13)

10 篇文章 0 订阅

java.util.concurrent包系列文章
JUC—ThreadLocal源码解析(JDK13)
JUC—ThreadPoolExecutor线程池源码解析(JDK13)
JUC—各种锁(JDK13)
JUC—原子类Atomic*.java源码解析(JDK13)
JUC—CAS源码解析(JDK13)
JUC—ConcurrentHashMap源码解析(JDK13)
JUC—CopyOnWriteArrayList源码解析(JDK13)
JUC—并发队列源码解析(JDK13)
JUC—多线程下控制并发流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)


本篇偏概念性,附带部分源码。

常用的锁

常用的2种加锁方式synchronized和Lock。

synchronized的缺点

  • 效率低,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程
  • 不够灵活,加锁和释放的时机单一,每个锁仅有单一的条件(某个对象)
  • 无法知道是否成功获取到锁

Lock作为synchronized的一种补充,他们都是可重入锁。
Lock的主要方法

  • lock():获取锁,如果锁被其他线程获取,则等待。lock()不会像synchronized一样在异常时自动释放锁,必须在finally中释放锁。lock()方法不能被中断,一旦陷入死锁,lock()就会陷入永久等待
  • tryLock():尝试获取锁,如果当前锁没有被其他线程占用,则获取成功,则返回true,否则返回false,代表锁获取失败。不等待,立刻返回
  • tryLock(long time,TimeUnit unit):超时就放弃
  • lockInterruptibly():相当于tryLock(long time,TimeUnitunit)把超时时间设置为无限。在等待锁的过程中可以被中断。 unlock():必须在finally中释放锁

锁的分类

在这里插入图片描述

互斥同步锁(悲观锁),

会锁住被操作的对象
缺点

  • 阻塞和唤醒带来的性能劣势
  • 可能会陷入永久阻塞,死锁

例子

  • Java中悲观锁的实现就是synchronized和Lock相关类
  • 数据库中selec for update也是悲观锁

适合于并发写很多的情况,适用于临界区持锁有时间比较长的情况,可以避免大量的无用自旋操作

非互斥同步锁(乐观锁)

不会锁住被操作的对象,在更新时会去检查我修改期间数据有没有被其他线程修改过,如果没有被修改过,就正常修改数据,如果发现数据被修改过,就会选择放弃,重试,报错等策略。乐观锁的实现一般都是使用CAS实现的。
缺点

  • 可能会导致大量的无用的自旋操作,消耗CPU资源

例子

  • Java中乐观锁的实现就是原子类和并发容器等
  • 数据库中version来控制更新就是乐观锁

适合于并发写入少,读很多的情况,不加锁能让读取性能大幅提高

可重入锁

同一个线程可以多次获取同一把锁,synchronized和ReentrantLock都是可重入锁。state次数+1。

公平锁与非公平锁

公平是指按照线程请求的顺序来分配锁,非公平是指不完全按照请求的顺序分配锁,在一定情况下,可以插队。设计非公平锁是为了提高效率,避免唤醒线程带来的空档期。把已经挂起的线程唤醒的那段时间是有开销的。如果是公平的,这段时间谁都没办法拿到锁。ReentrantLock默认非公平锁。

  • tryLock():本身是自带插队熟悉的。即时已经等待队列中已经有其他线程了。

在这里插入图片描述

公平锁在获取锁之前会判断有没有线程在队列中
在这里插入图片描述

非公平锁不管有没有线程在队列中都直接尝试去获取锁

在这里插入图片描述

共享锁和排他锁
  • 排他锁:又叫独占锁,独享锁。获取锁之后可以做修改查询。
  • 共享锁:又叫读锁。可以查看但是不能修改数据。

ReentrantReadWriteLock
读写锁的规则

  • 多个线程可以同时获取读锁
  • 如果一个先获得了读锁,其他线程无法申请写锁,会等待。
  • 如果一个线程获取了写锁,其他线程都不能获取写,读锁。
  • 要么一个多个读。要么一个写。多读一写。

读写锁插队策略
公平锁:不允许插队
非公平锁:

  • 写锁随时插队读
  • 锁仅在等待队列头节点不是想获取写锁的线程的时候可以插队

公平锁的情况下读写都需要判断队列是否有等待线程

在这里插入图片描述

非公平锁
写线程不关心等待队列是否有线程在等待,可以插队。
读线程需要判断等待队列头结点是否是排他锁(读锁)。

在这里插入图片描述

锁的升降级
支持锁的降级不支持升级,线程拿到写锁正在执行,中途需要执行某一个读锁锁定的方法,是可以的。不用先释放写锁,再去尝试获取这个读锁。提高了整体效率。在持有写锁的同时获取读锁。
如果支持升级的话容易造成死锁。两个线程都持有读锁,同时又都升级写锁,都要等待对方释放读锁,就会造成死锁。

自旋锁和阻塞锁
  • 自旋锁:阻塞和唤醒一个线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。让线程自旋,如果在自旋完成后前面锁定同步资源的线程 已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销。但是如果长时间自旋,也会耗费CPU资源。原子类Atomic*,就是利用无限循环加上CAS实现自旋。
  • 阻塞锁:如果没拿到锁,会直接把线程阻塞,直到被唤醒
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值