重入锁(ReentrantLock)
ReentrantLock是一种可重入的互斥锁,并且加锁是一种显式操作,对逻辑控制的灵活性远远大于synchronized关键字。重入锁是可以完全替代synchronized。并且重入锁的性能是远高于synchronized的,但是jdk6.0开始,jdk对synchronized做了大量的优化,使得两者性能差距不大。另外,ReentrantLock可结合Condition、以及提供了中断响应、锁申请等待限时、公平锁等。
公平锁、非公平锁(ReentrantLock)
ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体现在获取锁的机制上是否公平。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
运行如下代码,对比公平锁和非公平锁的运行结果
public static void main(String[] args) throws InterruptedException {
Lock noFairReentrantLock = new ReentrantLock();
Lock fairReentrantLock = new ReentrantLock(true);
Thread[] threads = new Thread[10];
for(int i=0;i<10;i++){
threads[i] = new Thread(()->{
System.out.println(Thread.currentThread().getName()+" [start]");
fairReentrantLock.lock();//也可以切换为非公平锁,观察运行结果
try {
System.out.println(Thread.currentThread().getName()+" [获得锁]");
}finally {
fairReentrantLock.unlock();
}
});
}
for(int i=0;i<5;i++){
threads[i].start();
}
for(int i=0;i<5;i++){
threads[i].join();
}
}
}
重入锁的中断响应功能
对于synchronized块来说,要么获取到锁执行,要么持续等待。而重入锁的中断响应功能就合理地避免了这样的情况。比如,一个正在等待获取锁的线程被“告知”无须继续等待下去,就可以停止工作了。
下面我们看一个利用中断响应功能解决的一个死锁问题
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
//线程t1和t2构成了死锁,此时我们可以中断的方式解决死锁问题
Runnable runnable = ()->{
try {
if(Thread.currentThread().getName().equals("t1")){
lock1.lockInterruptibly(); //在加锁的过程中仍然可以相应中断
Thread.sleep(100);
lock2.lockInterruptibly();
}else{
lock2.lockInterruptibly();
Thread.sleep(100);
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (lock1.isHeldByCurrentThread()) lock1.unlock();
if (lock2.isHeldByCurrentThread()) lock2.unlock();
}
};
Thread t1 = new Thread(runnable,"t1");
Thread t2 = new Thread(runnable,"t2");
t1.start();t2.start();
Thread.sleep(1000);
//以中断的方式解决死锁问题
t2.interrupt();
}
锁申请等待限时
可以使用 tryLock()或者tryLock(long timeout, TimeUtil unit) 方法进行一次限时的锁等待。
- tryLock(),线程尝试获取锁,如果获取到锁则继续执行,如果锁被其他线程持有,则立即返回false,也就是不会使当前线程等待,所以不会产生死锁。
- tryLock(long timeout, TimeUtil unit),表示在指定时长内获取到锁则继续执行,如果等待指定时长后还没有获取到锁则返回false。
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Runnable runnable = ()->{
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒
Thread.sleep(2000);
} else {
System.err.println(Thread.currentThread().getName() + "获取锁失败!");
}
} catch (Exception e) {
if (lock.isHeldByCurrentThread()) lock.unlock();
}
};
Thread t1 = new Thread(runnable,"t1");
Thread t2 = new Thread(runnable,"t2");
t1.start();t2.start();
}
ReentrantLock 配合 Condition 使用
配合关键字synchronized使用的方法如:await()、notify()、notifyAll(),同样配合ReentrantLock 使用的Conditon提供了以下方法:
public interface Condition {
void await() throws InterruptedException; // 类似于Object.wait()
void awaitUninterruptibly(); // 与await()相同,但不会再等待过程中响应中断
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal(); // 类似于Obejct.notify()
void signalAll();
}
ReentrantLock 配合 Condition的例子
public static void main(String[] args) throws InterruptedException {
ReentrantLock lock = new ReentrantLock(true);
Condition condition = lock.newCondition();
Thread t = new Thread(()->{
try {
lock.lock();
System.err.println(Thread.currentThread().getName() + "-线程开始等待...");
condition.await();
System.err.println(Thread.currentThread().getName() + "-线程继续进行了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1");
t.start();
Thread.sleep(1000);
System.err.println("过了1秒后...");
lock.lock();
condition.signal(); // 调用该方法前需要获取到创建该对象的锁否则会产生java.lang.IllegalMonitorStateException异常
lock.unlock();
}
ReentrantLock函数列表
// 创建一个 ReentrantLock ,默认是“非公平锁”。
ReentrantLock()
// 创建策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。
ReentrantLock(boolean fair)
// 查询当前线程保持此锁的次数。
int getHoldCount()
// 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
protected Thread getOwner()
// 返回一个 collection,它包含可能正等待获取此锁的线程。
protected Collection<Thread> getQueuedThreads()
// 返回正等待获取此锁的线程估计数。
int getQueueLength()
// 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 返回等待与此锁相关的给定条件的线程估计数。
int getWaitQueueLength(Condition condition)
// 查询给定线程是否正在等待获取此锁。
boolean hasQueuedThread(Thread thread)
// 查询是否有些线程正在等待获取此锁。
boolean hasQueuedThreads()
// 查询是否有些线程正在等待与此锁有关的给定条件。
boolean hasWaiters(Condition condition)
// 如果是“公平锁”返回true,否则返回false。
boolean isFair()
// 查询当前线程是否保持此锁。
boolean isHeldByCurrentThread()
// 查询此锁是否由任意线程保持。
boolean isLocked()
// 获取锁。
void lock()
// 如果当前线程未被中断,则获取锁。
void lockInterruptibly()
// 返回用来与此 Lock 实例一起使用的 Condition 实例。
Condition newCondition()
// 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
boolean tryLock()
// 如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。
boolean tryLock(long timeout, TimeUnit unit)
// 试图释放此锁。
void unlock()
来源:oschina
链接:https://my.oschina.net/u/2634007/blog/1944196