一、vs synchronized
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量
语法:
// 获取锁reentrantLock.lock();try {// 临界区} finally {// 释放锁reentrantLock.unlock();}
二、可重入
连续三次上锁。
@Slf4j(topic = "c.test")
public class Reentrant {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {lock.lock();try {m1();log.debug("上锁");} finally {lock.unlock();}}public static void m1(){lock.lock();try {log.debug("上锁");m2();} finally {lock.unlock();}}public static void m2(){lock.lock();try {log.debug("上锁");} finally {lock.unlock();}}
}
可以发现,上锁都成功了。
三、可打断
使用lockInterruptibly方法,如果无竞争那么就会获得lock锁;有有竞争会进入阻塞队列,可以被其他线程用interrupt方法打断。
@Slf4j(topic = "c.test")
public class Reentrant {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("启动");try {lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("我被打断了");return;}try {log.debug("获得了锁");} finally {//释放锁lock.unlock();}}, "t1");lock.lock();t1.start();try {Thread.sleep(1000);t1.interrupt();}finally {lock.unlock();}}
}
如果用lock方法那么是无法被打断的,会一直等待。
四、锁超时
使用trylock方法,尝试获得锁,如果能获得锁就获得,返回true,否则返回false。
立即失败:
@Slf4j(topic = "c.test")
public class Reentrant {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("启动");if(!lock.tryLock()){log.debug("没获得锁!");return;}try {log.debug("获得了锁");} finally {//释放锁lock.unlock();}}, "t1");lock.lock();t1.start();try {Thread.sleep(1000);}finally {lock.unlock();}}
}
超时失败:
lock.tryLock(1, TimeUnit.SECONDS),第一个参数是数字,第二个参数是单位。
@Slf4j(topic = "c.test")
public class Reentrant {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("启动");try {if(!lock.tryLock(1, TimeUnit.SECONDS)){log.debug("没获得锁!");return;}}catch (InterruptedException e){e.printStackTrace();}try {log.debug("获得了锁");} finally {//释放锁lock.unlock();}}, "t1");lock.lock();t1.start();try {Thread.sleep(1000);}finally {lock.unlock();}}
}
锁超时可以用来优化哲学家问题:让Chopstick继承ReentrantLock,然后使用tryLock优化。
五、公平锁
可以加一个boolean参数,true代表公平锁,默认是不公平的。
ReentrantLock lock = new ReentrantLock(true);
但是一般不用,公平锁会降低并发度。
六、条件变量
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的。
wait和notify的模板语法中,每次都要notifyAll,然后所有的wait线程都被唤醒,如果条件没满足,就是虚假唤醒。
但是使用了多个条件变量,我们就不需要盲目地唤醒所有线程了。
使用newCondition()创建一个条件变量,这个案例中模拟两个线程,一个线程需要吃苹果才能工作,一个线程需要卫生纸才能工作。
private static ReentrantLock lock = new ReentrantLock();static Condition hasApple = lock.newCondition();static Condition hasTissue = lock.newCondition();static boolean apple = false;static boolean tissue = false;
线程1:hasApple.await();方法,可以让当前线程进入等待。
new Thread(()->{try {lock.lock();while(!apple){try {hasApple.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("苹果来了,开始干活");}finally {lock.unlock();}}, "APPLE").start();
还需要一个唤醒线程,这里就可以看出来条件变量的优势,只需要唤醒hasApple中的线程即可,不会导致hasTissue中的线程被虚假唤醒。
private static void sendApple(){lock.lock();try {log.debug("送苹果来了");apple = true;hasApple.signal();}finally {lock.unlock();}}
完整代码:
@Slf4j(topic = "c.test")
public class Reentrant {private static ReentrantLock lock = new ReentrantLock();static Condition hasApple = lock.newCondition();static Condition hasTissue = lock.newCondition();static boolean apple = false;static boolean tissue = false;public static void main(String[] args) throws InterruptedException {new Thread(()->{try {lock.lock();while(!apple){try {hasApple.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("苹果来了,开始干活");}finally {lock.unlock();}}, "APPLE").start();new Thread(()->{try {lock.lock();while(!tissue){try {hasTissue.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("卫生纸来了,开始干活");}finally {lock.unlock();}}, "TISSUE").start();Thread.sleep(1000);sendApple();Thread.sleep(2000);sendTissue();}private static void sendApple(){lock.lock();try {log.debug("送苹果来了");apple = true;hasApple.signal();}finally {lock.unlock();}}private static void sendTissue(){lock.lock();try {log.debug("送卫生纸来了");tissue = true;hasTissue.signal();}finally {lock.unlock();}}
}
输出: