From fdb2e9404cfd529b9a3d30b1611373423a496fed Mon Sep 17 00:00:00 2001 From: luoxiang <2806718453@qq.com> Date: Tue, 26 Nov 2019 23:28:26 +0800 Subject: [PATCH] =?UTF-8?q?java=E5=B9=B6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../heibaiying/condition/AwaitAndSignal.java | 9 +- .../waitAndNotify/J5_NotifyAll.java | 1 - notes/Java_并发.md | 189 ++++++++++++++---- 3 files changed, 152 insertions(+), 47 deletions(-) diff --git a/code/Java/java-concurrency/src/main/java/com/heibaiying/condition/AwaitAndSignal.java b/code/Java/java-concurrency/src/main/java/com/heibaiying/condition/AwaitAndSignal.java index da9dfea..d3017cc 100644 --- a/code/Java/java-concurrency/src/main/java/com/heibaiying/condition/AwaitAndSignal.java +++ b/code/Java/java-concurrency/src/main/java/com/heibaiying/condition/AwaitAndSignal.java @@ -17,9 +17,9 @@ public class AwaitAndSignal { try { lock.lock(); String threadName = Thread.currentThread().getName(); - System.out.println(threadName + "等待通知..."); + System.out.println(threadName + "线程等待通知..."); condition.await(); - System.out.println(threadName + "获得锁"); + System.out.println(threadName + "线程后续操作"); } catch (InterruptedException e) { e.printStackTrace(); } finally { @@ -31,9 +31,10 @@ public class AwaitAndSignal { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new IncreaseTask()); thread1.start(); - Thread.sleep(2000); - // 必须要再次获取该重入锁,否则会抛出IllegalMonitorStateException异常 + Thread.sleep(1000); + System.out.println("主线程开始操作"); lock.lock(); + System.out.println("主线程唤醒"); condition.signal(); lock.unlock(); } diff --git a/code/Java/java-concurrency/src/main/java/com/heibaiying/waitAndNotify/J5_NotifyAll.java b/code/Java/java-concurrency/src/main/java/com/heibaiying/waitAndNotify/J5_NotifyAll.java index 7001cbb..3b69bea 100644 --- a/code/Java/java-concurrency/src/main/java/com/heibaiying/waitAndNotify/J5_NotifyAll.java +++ b/code/Java/java-concurrency/src/main/java/com/heibaiying/waitAndNotify/J5_NotifyAll.java @@ -36,7 +36,6 @@ public class J5_NotifyAll { System.out.println("对象object唤醒"); // 如果是object.notify()则是随机唤醒任意一个等待 object.notifyAll(); - } }).start(); } diff --git a/notes/Java_并发.md b/notes/Java_并发.md index 4377c1c..32a555d 100644 --- a/notes/Java_并发.md +++ b/notes/Java_并发.md @@ -453,48 +453,6 @@ ReentrantLock 即支持公平锁也支持非公平锁,公平锁在调度时候 private static ReentrantLock fairLock = new ReentrantLock(true); ``` -ReentrantLock 锁通常可以配合 `condition` 来使用,从而实现有条件的等待,示例如下: - -```java -public class AwaitAndSignal { - - private static ReentrantLock lock = new ReentrantLock(); - private static Condition condition = lock.newCondition(); - - static class IncreaseTask implements Runnable { - @Override - public void run() { - try { - lock.lock(); - String threadName = Thread.currentThread().getName(); - System.out.println(threadName + "等待通知..."); - condition.await(); - System.out.println(threadName + "获得锁"); - } catch (InterruptedException e) { - e.printStackTrace(); - } finally { - lock.unlock(); - } - } - } - - public static void main(String[] args) throws InterruptedException { - Thread thread1 = new Thread(new IncreaseTask()); - thread1.start(); - Thread.sleep(2000); - // 必须要再次获取该重入锁,否则会抛出IllegalMonitorStateException异常 - lock.lock(); - condition.signal(); - lock.unlock(); - } -} - -// 输出 -Thread-0等待通知... -// 睡眠2秒... -Thread-0获得锁 -``` - ### 6.3 读写锁 由于锁的排它性,导致多个线程无法以安全的方式并发地读取共享变量,这不利于提高系统的并发能力,因此产生了读写锁: @@ -599,8 +557,155 @@ public class ReadWriteLock { ### 7.1 等待与通知 +为了支持多线程之间的协作,JDK 中提供了两个非常重要的方法:`wait()` 和 `notify()` ,这两个方法定义在 `Object` 类中,这意味着任何 Java 对象都可以调用者两个方法。如果一个线程调用了 `object.wait()` 方法,那么它就会进入该对象的等待队列中,这个队列中可能包含了多个线程,此时代表多个线程都在等待同一个对象;当 `object.notify()` 方法被调用时,它就会从这个等待队列中**随机**唤醒一个线程。 + +需要特别注意的是在调用这两个方法时,它们都必须位于对应对象的 synchronzied 语句中,因为这两个对象在调用前都需要获得对应对象的监视器,过程如下: + +TODO + +使用示例如下: + +```java +public class J3_WaitAndNotify { + + private static final Object object = new Object(); + + public static void main(String[] args) { + new Thread(() -> { + synchronized (object) { + try { + System.out.println("对象object等待"); + object.wait(); + System.out.println("线程1后续操作"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + new Thread(() -> { + synchronized (object) { + System.out.println("线程2开始操作"); + System.out.println("对象object唤醒"); + object.notify(); + } + }).start(); + } +} + +// 输出 +对象object等待 +线程2开始操作 +对象object唤醒 +线程1后续操作 +``` + +`notify()` 表示随机唤醒任意一个等待线程,如果想要唤醒所有等待线程,则可以使用 `notifyAll()` 方法: + +```java +public class J5_NotifyAll { + + private static final Object object = new Object(); + + public static void main(String[] args) { + new Thread(() -> { + synchronized (object) { + try { + System.out.println("对象object在线程1等待"); + object.wait(); + System.out.println("线程1后续操作"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + new Thread(() -> { + synchronized (object) { + try { + System.out.println("对象object在线程2等待"); + object.wait(); + System.out.println("线程2后续操作"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + new Thread(() -> { + synchronized (object) { + System.out.println("线程3开始操作"); + System.out.println("对象object唤醒"); + object.notifyAll(); + } + }).start(); + } +} + +// 输出 +对象object在线程1等待 +对象object在线程2等待 +线程3开始操作 +对象object唤醒 +线程2后续操作 +线程1后续操作 +``` + +在上面的示例中,由于有两个线程处于等待状态,所以 `notifyAll()` 的效果等价于调用 `notify()` 两次: + +```java +object.notify(); +object.notify(); +``` + ### 7.2 条件变量 +综上所述可以使用 `wait()` 和 `notify()` 配合内部锁 synchronized 来实现线程间的等待与唤醒,如果你使用的是显示锁而不是内部锁,此时可以使用 Condition 来实现同样的等待唤醒效果。Condition 接口中定义了如下方法: + ++ await(): ++ awaitUninterruptibly(): + +使用示例如下: + +```java +public class AwaitAndSignal { + + private static ReentrantLock lock = new ReentrantLock(); + private static Condition condition = lock.newCondition(); + + static class IncreaseTask implements Runnable { + @Override + public void run() { + try { + lock.lock(); + String threadName = Thread.currentThread().getName(); + System.out.println(threadName + "线程等待通知..."); + condition.await(); + System.out.println(threadName + "线程后续操作"); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + lock.unlock(); + } + } + } + + public static void main(String[] args) throws InterruptedException { + Thread thread1 = new Thread(new IncreaseTask()); + thread1.start(); + Thread.sleep(1000); + System.out.println("主线程开始操作"); + lock.lock(); + System.out.println("主线程唤醒"); + condition.signal(); + lock.unlock(); + } +} + +// 输出: +Thread-0线程等待通知... +主线程开始操作 +主线程唤醒 +Thread-0线程后续操作 +``` + ### 7.3 CountDownLatch ### 7.4 CyclicBarrier