ReentrantLock 可以替代synchrozied,可以完成同样的功能, 更灵活 手动上锁,手动释放锁
主要有以下几个特点:
- 必须手动释放锁
- 可以指定时间,要是时间段内没有申请到,就可以自己做相应处理
- 中断响应,锁定之后,别人可以打断
- 还可以把锁指定为公平锁,谁等的时间长,谁就会优先得到锁
- Condition条件
方法整理:
- lock():获取锁,如果已被占用,则等待
- lockInterruptibly():获取锁,优先响应中断
- tryLock():尝试获取锁,如果成功,返回true;失败,返回false。该方法不等待,立即返回
- tryLock(long time, TimeUnit unit):在给定时间内获取锁
- unLock():释放锁
1、Reentrantlock 必须要手动释放锁
使用 synchrozied 锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock(); //synchronized(this)
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
2、指定尝试申请锁时间
使用 tryLock 可以进行“尝试锁定”,如果无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
* 可以根据tryLock的返回值来判定是否锁定
* 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
*/
void m2() {
/*
boolean locked = lock.tryLock();
System.out.println("m2 ..." + locked);
if(locked) lock.unlock();
*/
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2 ..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked) lock.unlock();
}
}
3、中断响应
lock.lockInterruptibly() 对线程中断 interrupt() 做出响应
使用 lockInterruptibly() 则该线程在等待锁的过程中,如果被中断,则直接抛出中断异常来立即响应中断,由上层调用者处理中断。最后线程结束等锁
而使用 lock() ,则不会响应中断,默认自己处理中断,继续获取锁直到成功。
Thread t1 = new Thread(()->{
try {
lock.lock();
System.out.println("t1 start");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
System.out.println("t1 end");
} catch (InterruptedException e) {
System.out.println("t1 interrupted!");
} finally {
lock.unlock();
}
});
t1.start();
Thread t2 = new Thread(()->{
try {
//可以对interrupt()方法做出响应,然后结束等锁
lock.lockInterruptibly();
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (InterruptedException e) {
System.out.println("t2 interrupted!");
} finally {
lock.unlock();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打断线程2的等待
t2.interrupt();
4、可以指定为公平锁
公平锁:谁等的时间长谁就先得到锁
//参数为true表示为公平锁
private static ReentrantLock lock = new ReentrantLock(true);
5、Condition条件
Condition对象和Object.wait()/notify()的作用是大致相同的。wait/notify和synchronized合作使用,而condition和ReentrantLock相关联。通过Lock接口的newCondition可以生产与当前ReentrantLock绑定的Condition实例。
// 使当前线程等待,同时释放锁。
// 使用signal/signalAll重新获取锁。
// 当线程被中断时,也可以跳出等待
void await() throws InterruptedException
// 与await基本相同,只是不响应中断
void awaitUniterrutibly()
long awaitNanos(long nanosTimeout) throws InterruptedException
boolean await(long time, TimeUnit unit) throws InterruptedException
boolean awaitUtil(Date deadline) throws InterruptedException
// 唤醒等待线程
void signal()
void signalAll()
演示说明
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//子线程中
public void run{
try {
lock.lock();
condition.await();
System.out.println("Thread is going on");
} catch(InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//主线中
lock.lock(); //先获取锁
condition.signal();
lock.unlock(); //释放锁,让给被唤醒的线程,让可以继续执行
和wait/notify 一样,当线程使用 Condition.await() 和 signal() 时必须先获取锁。当调用condition.await() 后,会释放这把锁;当调用condition.signal()后,会从当前Condition对象的等待队列中唤醒一个线程,唤醒后,会重新尝试获取与之绑定的重入锁,一旦成功,就可以继续执行了。