相对于 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; } @Override public 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; } @Override public 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();
- }
- }
@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(); } }park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』, 不需要『同步对象』和『运行标记』
线程 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 2
- b 2 3
- c 3 1
- */
- class WaitNotify {
- // 打印 a 1 2
- public 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;
- }
- }