相对于 synchronized 具备如下特定:
(1)可中断
(2)可以设置超市时间
(3)可以设置为公平锁
(4)支持多个条件变量
与 synchronized 一样,都支持可重入
基本语法
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
如果是不可重入,那么第二次获得锁时,自己也会被锁挡住。
@Slf4j(topic = "c.Test22") public class Test22 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) {lock.lock();try {log.debug("enter main");m1();} finally {lock.unlock();}}public static void m1(){lock.lock();try {log.debug("enter m1");m2();} finally {lock.unlock();}}public static void m2(){lock.lock();try {log.debug("enter m2");} finally {lock.unlock();}} }
@Slf4j(topic = "c.Test22") public class Test22 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {//如果没有竞争那么此方法就会获取lock对象锁//如果有竞争就进入阻塞队列,可以被其它线程用interruput方法打断log.debug("尝试获取锁");lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();log.debug("没有获得锁,返回");return;}try {log.debug("获取到锁");} finally {lock.unlock();}},"t1");lock.lock();t1.start();Thread.sleep(1000);log.debug("打断 t1");t1.interrupt();} }注意如果是不可中断(lock()方法)模式,那么即使使用了 interrupt 也不会让等待中断
@Slf4j(topic = "c.Test22") public class Test22 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("尝试获取锁");// 立刻尝试获取锁boolean tryLock = lock.tryLock();if (!tryLock){log.debug("获取不到锁");return;}try {log.debug("获得到锁");} finally {lock.unlock();}},"t1");log.debug("获取锁");lock.lock();t1.start();} }
@Slf4j(topic = "c.Test22") public class Test22 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("尝试获取锁");try {// 2s后尝试获取锁boolean tryLock = lock.tryLock(2, TimeUnit.SECONDS);if (!tryLock){log.debug("获取不到锁");return;}} catch (InterruptedException e) {e.printStackTrace();log.debug("获取不到锁");return;}try {log.debug("获得到锁");} finally {lock.unlock();}},"t1");log.debug("获取锁");lock.lock();t1.start();} }
使用 tryLock 解决哲学家就餐问题@Slf4j(topic = "c.Test23") public class Test23 {public static void main(String[] args) {Chopstick c1 = new Chopstick("1");Chopstick c2 = new Chopstick("2");Chopstick c3 = new Chopstick("3");Chopstick c4 = new Chopstick("4");Chopstick c5 = new Chopstick("5");new Philosopher("苏格拉底", c1, c2).start();new Philosopher("柏拉图", c2, c3).start();new Philosopher("亚里士多德", c3, c4).start();new Philosopher("赫拉克利特", c4, c5).start();new Philosopher("阿基米德", c5, c1).start(); } }@Slf4j(topic = "c.Philosopher") class Philosopher extends Thread {Chopstick left;Chopstick right;public Philosopher(String name, Chopstick left, Chopstick right) {super(name);this.left = left;this.right = right;}@Overridepublic void run() {while (true) {// 尝试获得左手筷子if(left.tryLock()) {try {// 尝试获得右手筷子if(right.tryLock()) {try {eat();} finally {right.unlock();}}} finally {left.unlock(); // 释放自己手里的筷子}}}}Random random = new Random();private void eat() {log.debug("eating...");Sleeper.sleep(0.5);} }class Chopstick extends ReentrantLock {String name;public Chopstick(String name) {this.name = name;}@Overridepublic String toString() {return "筷子{" + name + '}';} }
ReentrantLock 默认是不公平的
// true:公平 // false(默认):不公平 ReentrantLock lock = new ReentrantLock(true);公平锁一般没有必要,会降低并发度,后面分析原理时会讲解。
synchronized 中也有条件变量,就是我们讲原理的 waitSet 休息室,当条件不满足时进入等待。
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比:
(1)synchronized 是那些不满足条件的线程都在一间休息室等消息
(2)而 ReentrantLock 支持多间休息室,唤醒时也是按休息室来唤醒的
使用要点:(1)await 前需要获得锁
(2)await 执行后,会释放锁,进入 conditionObject 等待
(3)await 的线程被唤醒(或打断、或超时)去重新竞争 lock 锁
(4)竞争 lock 锁成功后,从 await 后继续执行
@Slf4j(topic = "c.Test22") public class Test22 {private static ReentrantLock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {// 创建一个新的条件变量(休息室)Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();lock.lock();// 进入休息室等待condition1.await();condition1.signal();condition1.signalAll();} }
@Slf4j(topic = "c.Test24") public class Test24 {static final Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;static ReentrantLock ROOM = new ReentrantLock();// 等待烟的休息室static Condition waitCigaretteSet = ROOM.newCondition();// 等外卖的休息室static Condition waitTakeoutSet = ROOM.newCondition();public static void main(String[] args) {new Thread(() -> {ROOM.lock();try {log.debug("有烟没?[{}]", hasCigarette);while (!hasCigarette) {log.debug("没烟,先歇会!");try {waitCigaretteSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以开始干活了");} finally {ROOM.unlock();}}, "小南").start();new Thread(() -> {ROOM.lock();try {log.debug("外卖送到没?[{}]", hasTakeout);while (!hasTakeout) {log.debug("没外卖,先歇会!");try {waitTakeoutSet.await();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("可以开始干活了");} finally {ROOM.unlock();}}, "小女").start();sleep(1);new Thread(() -> {ROOM.lock();try {hasTakeout = true;waitTakeoutSet.signal();} finally {ROOM.unlock();}}, "送外卖的").start();sleep(1);new Thread(() -> {ROOM.lock();try {hasCigarette = true;waitCigaretteSet.signal();} finally {ROOM.unlock();}}, "送烟的").start();}}
@Slf4j(topic = "c.Test25") public class Test25 {static final Object lock = new Object();// 表示 t2 是否运行过static boolean t2runned = false;public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (lock) {while (!t2runned) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug("1");}}, "t1");Thread t2 = new Thread(() -> {synchronized (lock) {log.debug("2");t2runned = true;lock.notify();}}, "t2");t1.start();t2.start();} }
park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』, 不需要『同步对象』和『运行标记』@Slf4j(topic = "c.Test26") public class Test26 {public static void main(String[] args) {Thread t1 = new Thread(() -> {LockSupport.park();log.debug("1");}, "t1");t1.start();new Thread(() -> {log.debug("2");LockSupport.unpark(t1);},"t2").start();} }
线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现
@Slf4j(topic = "c.Test27") public class Test27 {public static void main(String[] args) {WaitNotify wn = new WaitNotify(1, 5);new Thread(() -> {wn.print("a", 1, 2);}).start();new Thread(() -> {wn.print("b", 2, 3);}).start();new Thread(() -> {wn.print("c", 3, 1);}).start();} }/* 输出内容 等待标记 下一个标记a 1 2b 2 3c 3 1*/ class WaitNotify {// 打印 a 1 2public void print(String str, int waitFlag, int nextFlag) {for (int i = 0; i < loopNumber; i++) {synchronized (this) {while(flag != waitFlag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.print(str);flag = nextFlag;this.notifyAll();}}}// 等待标记private int flag; // 2// 循环次数private int loopNumber;public WaitNotify(int flag, int loopNumber) {this.flag = flag;this.loopNumber = loopNumber;} }
public class Test30 {public static void main(String[] args) throws InterruptedException {AwaitSignal awaitSignal = new AwaitSignal(5);Condition a = awaitSignal.newCondition();Condition b = awaitSignal.newCondition();Condition c = awaitSignal.newCondition();new Thread(() -> {awaitSignal.print("a", a, b);}).start();new Thread(() -> {awaitSignal.print("b", b, c);}).start();new Thread(() -> {awaitSignal.print("c", c, a);}).start();Thread.sleep(1000);awaitSignal.lock();try {System.out.println("开始...");a.signal();} finally {awaitSignal.unlock();}} }class AwaitSignal extends ReentrantLock{private int loopNumber;public AwaitSignal(int loopNumber) {this.loopNumber = loopNumber;}// 参数1 打印内容, 参数2 进入哪一间休息室, 参数3 下一间休息室public void print(String str, Condition current, Condition next) {for (int i = 0; i < loopNumber; i++) {lock();try {current.await();System.out.print(str);next.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {unlock();}}} }
@Slf4j(topic = "c.Test31") public class Test31 {static Thread t1;static Thread t2;static Thread t3;public static void main(String[] args) {ParkUnpark pu = new ParkUnpark(5);t1 = new Thread(() -> {pu.print("a", t2);});t2 = new Thread(() -> {pu.print("b", t3);});t3 = new Thread(() -> {pu.print("c", t1);});t1.start();t2.start();t3.start();LockSupport.unpark(t1);} }class ParkUnpark {public void print(String str, Thread next) {for (int i = 0; i < loopNumber; i++) {LockSupport.park();System.out.print(str);LockSupport.unpark(next);}}private int loopNumber;public ParkUnpark(int loopNumber) {this.loopNumber = loopNumber;} }
下一篇:008. 子集