JUC并发编程-Lock锁

Lock详解

简介

java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的)

LockReadWriteLock两大锁的根接口

  • Lock代表实现类是ReentrantLock可重入锁
  • ReadWriteLock(读写锁)的代表实现类是ReentrantReadWriteLock

JUC并发编程-Lock锁_第1张图片

同步实现原理

lock只能被一个线程获取,当一个线程执行lock.lock()后其他线程再尝试获取会进入lock对象中的等待队列,从而达到同步效果,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。

img

  • lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)
  • lock获取锁的过程:本质上是通过CAS(两次)来修改锁状态值,如果当场没获取到,会将该线程放在线程等待链表中自旋+CAS尝试获取锁。应该不是所有线程都自旋吧???(只有head才会自旋尝试加锁,并且自旋执行也不是无限自旋,对于head节点来说只会尝试3次获取锁为什么,而对于非head节点来说只会尝试获取一次为什么,获取失败则lcoksupport.park ,而CAS仅仅是针对尝试获取锁
  • lock释放锁的过程:CAS修改状态值,调整等待链表。因为无竞争所以没有使用CAS。

Lock接口实现类的使用

Lock接口有6个方法:

// 获取锁  
void lock()   

// 释放锁  
void unlock()  

// 如果当前线程未被中断,则获取锁,可以响应中断  
void lockInterruptibly()   

// 返回绑定到此 Lock 实例的新 Condition 实例  
Condition newCondition()   

// 仅在调用时锁为空闲状态才获取该锁,可以响应中断  
boolean tryLock()   

// 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁  
boolean tryLock(long time, TimeUnit unit)   
获取释放锁
Lock lock=new ReentrantLock();
lock.lock();
try{
    //处理任务
}catch(Exception ex){
     
}finally{
    lock.unlock();   //释放锁
}

Synchronized与Lock

构成

synchronized是关键字属于JVM层面

  • monitorenter(底层通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象只有在同步块或方法中调用wait/notify等方法)
  • monitorexit

Lock是具体类(java.util.concurrent.Locks.lock)是api层面的锁

使用方法
  • synchronized 不需要手动释放锁,当synchronized代码执行完后系统会自动让线程释放对锁的占用
  • ReentrantLock需要手动释放锁若没有主动释放锁,就有可能导致出现死锁现象。需要Lock()和unlock()方法配合try/finally语句块来完成
等待是否可中断
  • synchronized不可中断,除非抛出异常或者正常运行完成
  • ReentrantLock 可中断
    • 设置超时方法tryLock(long timeout,TimeUnit unit)
    • lockInterruptibly()放代码块中,调用interrupt()方法可中断
加锁是否公平
  • synchronized非公平锁
  • ReentrantLock两者都可以,默认非公平锁,构造方法可以传入boolean值,true为公平锁,false为非公平锁
锁绑定多个条件Condition
  • synchronized没有,要么随机唤醒一个线程要么唤醒全部线程
  • ReentrantLock用来实现分组唤醒需要的线程们,可以实现精确唤醒

synchronized的缺陷

  • 效率低:锁的释放情况少,只有代码执行完毕或者异常结束才会释放锁;试图获取锁的时候不能设定超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时
  • 不够灵活:加锁和释放的时机单一,每个锁仅有一个单一的条件(某个对象),相对而言,读写锁更加灵活
  • 无法知道是否成功获得锁,相对而言,Lock可以拿到状态,如果成功获取锁,…,如果获取失败,…

Lock解决相应问题

Lock类这里不做过多解释,主要看里面的4个方法:

  • lock(): 加锁
  • unlock(): 解锁
  • tryLock(): 尝试获取锁,返回一个boolean值
  • tryLock(long,TimeUtil): 尝试获取锁,可以设置超时

Synchronized只有锁只与一个条件(是否获取锁)相关联,不灵活,后来Condition与Lock的结合解决了这个问题。

多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。高并发的情况下会导致性能下降。ReentrantLock的lockInterruptibly()方法可以优先考虑响应中断。 一个线程等待时间过长,它可以中断自己,然后ReentrantLock响应这个中断,不再让这个线程继续等待。有了这个机制,使用ReentrantLock时就不会像synchronized那样产生死锁了。

你可能感兴趣的:(JUC并发编程,java,开发语言,后端,juc)