【Java】Lock体系

Lock体系

1、死锁产生条件
  • 1、互斥:共享资源只能被一个线程占用
  • 2、占有且等待:线程A已经取得共享资源X,在等待获取资源Y时,不释放X
  • 3、不可抢占:线程A已经获取X之后,其他线程不能强行抢占X
  • 4、循环等待:线程A占用X,线程B占用Y,A等待Y,B等待X
2、如何解决死锁问题?

只要破坏任何一个条件即可解决

为了方便的解决死锁问题,引出了Lock体系
Lock体系相较于synchronized独有的方法(特性)
1、响应中断

void lockInterruptibly() throws InterruptedException;

2、非阻塞式获取锁

boolean tryLock();

3、支持超时

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

Lock的使用格式

Lock lock = new ReentrantLock();
try {
    //同步代码块(临界区)
    lock.lock();
}catch(Exception e) {
}finally {
    // 显式解锁
    lock.unlock();
}

3、Lock接口常用的子类

1、ReentrantLock

  • 支持可重入锁,使用与synchronized基本一致
  • synchronized与ReentrantLock的关系与区别?
    • 1、都属于独占锁的实现,任意一个时刻只有一个线程能够获取到锁,都支持可重入
    • 2、synchronized属于JVM层面的管程实现,ReentrantLock属于Java语言层面实现的管程
    • 3、ReentrantLock有一些synchronized不具备的特性:响应中断、支持超时、支持非阻塞式的获取锁,支持公平锁,支持多个等待队列。

2、读写锁ReentrantReadWriteLock(读者写者模型)
读读异步,读写、写写同步。读锁的意义在于和写锁搭配使用,读锁不等于无锁。ReentrantReadWriteLock实现了读写锁,readLock()获取读锁、writeLock()获取写锁。使用HashMap+ReentrantReadWriteLock实现多线程缓存

  • 读锁:共享锁,任意一个时刻可以有多个不同线程获取锁
  • 写锁:独占锁,任意一个时刻只能有一个线程获取写锁
4、synchronized与Lock的关系与区别
  • 1、都可以实现独占锁的实现,都支持可重入锁
  • 2、Lock有一些synchronized不具备的特性:响应中断、支持超时、支持非阻塞式的获取锁Lock还支持公平锁,支持多个等待队列,支持读写锁
5、公平锁:等待时间最长的线程最先获取锁

ReentrantLock支持两种锁:公平锁和非公平锁。何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么 锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO。ReentrantLock的构造方法无参时是构造非公平锁, 源码为:

public ReentrantLock() {    
    sync = new NonfairSync(); 
}

另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:

public ReentrantLock(boolean fair) {    
    sync = fair ? new FairSync() : new NonfairSync(); 
}

任何一个对象都有两个队列,等待队列和同步队列

  • 同步队列:所有获取该对象Monitor失败的线程进入同步队列
  • 等待队列:调用wait()阻塞的线程进入等待队列

synchronized无法实现当等待队列中线程与此时刚创建线程的竞争公平问题,Lock体系可以解决,只需要在传参的时候传入一个true便可实现公平锁

6、生产-消费者模型(等待与唤醒机制)

任何一个java对象都天然继承于Object类,在线程间实现通信的往往会应用到Object的几个方法,比如 wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll()几个方法实现等待/通知机制,同样的, 在java Lock体系下依然会有同样的方法实现等待/通知机制。从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是java底层级别的,后者是语言级别的,具有更高的可控制性和扩展性。两者除了在使用方式上不同外,在功能特性上还是有很多的不同:

  • 1、Condition能够支持不响应中断,而通过使用Object方式不支持;
  • 2、Condition能够支持多个等待队列(new 多个Condition对象),而Object方式只能支持一个;
  • 3、Condition能够支持超时时间的设置,而Object不支持
6.1、参照Object的wait和notify/notifyAll方法,Condition也提供了同样的方法: 针对Object的wait方法
  • 1、void await() throws InterruptedException:当前线程进入等待状态,如果其他线程调用condition的signal或 者signalAll方法并且当前线程获取Lock从await方法返回,如果在等待状态中被中断会抛出被中断异常;
  • 2、long awaitNanos(long nanosTimeout):当前线程进入等待状态直到被通知,中断或者超时;
  • 3、boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位
  • 4、boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态直到被通知,中 断或者到了某个时间
6.2、针对Object的notify/notifyAll方法
  • 1、void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,如果在同步队 列中能够竞争到Lock则可以从等待方法中返回。
  • 2、void signalAll():与1的区别在于能够唤醒所有等待在condition上的线程
7、Lock体系的优势

synchronized + wait()/notify():必须在同步代码块或同步方法中执行调用线程进入等待队列,释放锁。任意一个锁对象只有一个等待队列,所有线程对象调用wait()都置入同一个等待队列,调用notify()或notifyAll()或唤醒不该唤醒的线程,或造成CPU资源的浪费。

使用Lock + Condition实现多生产消费模型,Lock体系中的Condition newCondition():每new一次就产生一个新的Condition(等待队列)对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值