死锁
死锁产生的条件
- 互斥:共享资源只能同时被一个线程占用
- 占有且等待:eg:拿到worker锁,不释放的同时去申请money锁
- 不可抢占:线程Thread1拿到对象锁x后,其他线程无法强行抢占x锁。
- 循环等待:线程T1拿到了资源X的锁,去申请Y的锁。线程T2拿到了资源Y的锁,去申请X的锁。
Lock-JDK1.5-基于Java语言层面实现的线程"锁"
产生背景:synchronized死锁
破坏不可抢占:
- 能够响应中断
- 支持超时
- 非阻塞式获取锁
Lock接口下的三个重要方法
- 响应中断 void lockInterruptibly();
- 支持超时 boolean tryLock(long time, TimeUnit unit)
- 非阻塞式获取锁 线程若获取不到锁,线程直接退出 boolean tryLock();
Lock体系使用的格式
Lock lock=new ReentrantLock();
try {
// 加锁
lock.lock();
}finally {
// 解锁
lock.unlock();
}
synchronized同步块执行完成或者遇到异常时锁会自动释放,而lock必须调用unlock()方法释放锁,因此在finally块中释放锁
Lock接口中的方法
- void lock(); //获取锁
- void lockInterruptibly()throws InterruptedException;//获取锁的过程相应中断
- boolean tryLock();//非阻塞式响应中断能立即返回,获取锁返回true,反之为false
- boolean tryLock(long time,TimeUnit unit);//超时获取锁,在超时时间内或为中断的情况下能获取锁
- Condition newCondition();//获取与lock绑定的等待通知事件,当前线程必须先获得锁才能等待,等待会释放锁,再次获取锁才能从等待中返回
公平锁
等待时间最长的线程最先获取到锁(synchronizd无法实现公平锁)
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) { //传入参数为true为公平锁
sync = fair ? new FairSync() :
new NonfairSync();
}
公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平锁则不一定,有可能刚释放的线程再次获取到锁
独占锁
synchronized,在任意一个时刻,只有一个线程可以拥有此锁
- void acquire(int arg); 独占式获取同步状态,如果获取失败则插入同步队列进行等待
- boolean tryAcquire(int arg); 获取锁成功返回true,否则返回false
- boolean release(int arg); 释放同步状态,该方法会唤醒在同步队列中的下一个节点
共享锁
在同一个时刻,可以有多个线程拥有锁
- void acquireShared(int arg) ; 共享式获取同步状态,与独占锁的区别在于同一个时刻有多个线程获取同步队列
- boolean releaseShared(int arg); 共享释放同步状态
读写锁-共享锁的一种实现
读锁共享:多个线程可以同时拿到读锁进行访问,当写线程拿到写锁开始工作时,所有读线程全部阻塞
写锁独占:任意一个时刻,只有一个线程可以拿到写锁
读读-不互斥,读写、写写-互斥
Condition:Lock体系的线程通信方式
- await():释放lock锁,将线程置入等待队列阻塞
- signal():随机唤醒一个处于等待状态的线程
- signalAll():唤醒所有等待线程
synchronized与lock的关系
- synchronized与Lock都是对象锁,都支持可重入锁
- Lock可以实现synchronized不具备的特性,如响应中断、支持超时、非阻塞的获取锁、公平锁、共享锁(读写锁,ReentrantReadWriteLock实现)
- Lock体系的Condition队列可以有多个(区分于synchronized只有一个等待队列),可以进一步提高效率,减少线程阻塞与唤醒带来的开销(唤醒了不该唤醒的线程)。获取一个lock锁的condition队列:lock.newCondition() : 产生一个新的Condition队列。