ReentrantLock锁和synchronized锁相比具有以下特点:
- 可被中断:即可以设置“可中断锁”,当t1线程获取不到锁对象处于Blocked状态时,可由另一个线程调用t1的interrupt方法将其中断,此时t1会抛出被打断异常。
- 可以设置超时时间:如果没有获取到锁对象处于Blocked状态时,t1线程可以自己决定要等待多久。
- 可以设置为公平锁。
- 支持多个条件变量,即不同线程可以处于不同的waitSet进行等待,唤醒时也可以按照waitSet唤醒。
ReetrantLock的打开方式:
1、可打断锁
//可打断
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
//如果没有竞争,此方法就会获取lock对象的锁
//如果有竞争,就进入阻塞队列,但可以被其他线程用interrupt方法打断
System.out.println("尝试获得锁ing");
//获得可打断锁
lock.lockInterruptibly();
} catch (InterruptedException e) {
//如果被锁被打断,就会抛出异常
System.out.println("我被打断了,没有获得锁");
e.printStackTrace();
return;
}
try {
System.out.println("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
//主线程先加锁
lock.lock();
t1.start();
Thread.sleep(1000);
//主线程打断t1,不让他Blocked下去,此时t1抛出异常进入catch块
System.out.println("打断t1");
t1.interrupt();
}
2、可超时锁
public static void main(String[] args) {
Thread t1= new Thread(()->{
//尝试获得锁,如果成功了,返回true,获得锁;失败了返回false
//如果传递时间参数(如下面代码),则会等待1s,如果能获取到,则获取,不能获得则不等待
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("没有获得锁");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果顺利走到这里,说明得到了锁
System.out.println("获得到了锁");
lock.unlock();
},"t1");
lock.lock();
System.out.println("主线程获得到锁");
t1.start();
lock.unlock();
}
3、多个waitSet
public class TestCondition_04 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
//创建两个新的条件变量(即新的休息室)
Condition condition = lock.newCondition();
Condition condition1 = lock.newCondition();
Thread t1 = new Thread(() -> {
lock.lock();
try {
//进入第一间休息室等待
condition1.await();
System.out.println("我被唤醒啦 ");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("我被打断啦");
}
finally {
lock.unlock();
}
}, "t1");
t1.start();
//先让主线程睡一会,确保t1线程已经进入waitSet
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//和notify一样,唤醒线程也需要先拿到锁
lock.lock();
//唤醒第一个waitSet的线程
condition1.signal();
//主线程必须要释放锁后t1才能进入执行后面的指令!
lock.unlock();
//测试t1是否释放了锁
Thread.sleep(1000);
lock.lock();
System.out.println("如果看到了我,说明t1线程把锁释放了");
lock.unlock();
}
}
切记:使用完ReentrantLock锁后,一定要把锁释放掉!不然线程虽然结束,但是锁并没有被释放。个人认为这是和synchronized的最大的不同,synchronized只要临界区执行完毕就会自动释放锁。
在waitSet中的线程,如果被唤醒或者被interrupt,都会进入entrySet进入Block状态竞争锁。竞争锁成功后,如果是被唤醒则执行后面的指令,如果是被interrupt,则执行catch{},最后一定要释放锁。