ReentrantLock 锁
实现 Lock 接口,使用时需导入 import java.util.concurrent.locks.*;
。
实现功能和 synchronized 关键字类似。但 synchronized 关键字是在 JVM 层面实现的,而 ReenTrantLock 是在 JDK 层面实现的。需要手动调用 lock 和 unlock 方法配合 try/finally 语句块来完成。
代码语言:javascript复制public class ReentrantLockTest {
// 创建锁对象
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 5; i ){
new Thread(new MyThread()).start();
}
}
static class MyThread implements Runnable {
@Override
public void run() {
try {
// 加锁,通常在 try 语句里完成
lock.lock();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() "excute");
} catch (InterruptedException e) {}
finally{
// 解锁,必须在 finally 语句里完成
lock.unlock();
}
}
}
}
}Copy to clipboardErrorCopied
ReenTrantLock 比 synchronized 增加了一些高级功能,主要有以下三点:
实现等待中断
调用 lockInterruptibly 方法上锁,线程中断标志置为 true 时会抛出 InterruptedException 异常并释放锁。防止线程因为无法获得锁而一直等待,常用来从外部破坏线程死锁。
代码语言:javascript复制public class ThreadDemo {
// 创建锁对象
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyThread(),"thread1");
Thread t2 = new Thread(new MyThread(),"thread2");
t1.start();
t2.start();
Thread.sleep(500);
// 提前中断线程
t2.interrupt();
}
static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() "begin");
try {
// 加可中断锁
lock.lockInterruptibly();
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() "out");
} finally{
try{
lock.unlock();
} catch(IllegalMonitorStateException e) {}
System.out.println(Thread.currentThread().getName() "end");
}
}
}
}Copy to clipboardErrorCopied
调用 tryLock 方法上锁,可以从线程内部破坏死锁,可以更好地解决死锁问题。
- 传入时间参数设定等待锁的时间,超时没有获得锁则中止。
- 无参则返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。
public class ThreadDemo {
// 创建锁对象
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyThread(),"thread1");
Thread t2 = new Thread(new MyThread(),"thread2");
t1.start();
t2.start();
}
static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() "begin");
try {
// 加锁失败直接退出
if(!lock.tryLock()) {
System.out.println(Thread.currentThread().getName() "out");
return;
}
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
try{
lock.unlock();
} catch(IllegalMonitorStateException e) {}
System.out.println(Thread.currentThread().getName() "end");
}
}
}
}Copy to clipboardErrorCopied
实现公平锁
允许先等待的线程先获取锁,防止线程因无法获得锁而一直等待。但由于性能优势,默认情况下仍使用非公平锁。在构造锁对象时添加参数 true 即可实现。
代码语言:javascript复制import java.util.concurrent.locks.*;
public class ReentrantLockTest {
// 创建锁对象,且声明为公平锁
static Lock lock = new ReentrantLock(true);
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 5; i ){
new Thread(new MyThread()).start();
}
}
static class MyThread implements Runnable {
@Override
public void run() {
lock.lock();
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() "excute");
} catch (InterruptedException e) {}
lock.unlock();
}
}
}
}Copy to clipboardErrorCopied
选择性通知
ReentrantLock 对象可以创建一个或多个 Condition 对象,实现线程间的等待通知机制。比 synchronized 关键字 使用 wait/notify 方法更为简便和易用。
线程获得 Lock 锁之后便可调用 Condition 接口的 await 方法释放锁并等待,直到有其他线程调用 Condition 的 signal 方法唤醒线程。通过设置多个 condition 对象,多个线程等待不同的 condition 对象,可以实现选择性地叫醒线程。
代码语言:javascript复制public class ThreadDemo {
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
lock.lock();
new Thread(new MyThread()).start();
System.out.println("主线程等待通知");
try {
condition.await();
} finally {
lock.unlock();
}
System.out.println("主线程恢复运行");
}
static class MyThread implements Runnable {
@Override
public void run() {
lock.lock();
try {
condition.signal();
System.out.println("子线程通知");
} finally {
lock.unlock();
}
}
}
}