zoukankan      html  css  js  c++  java
  • Java并发包——线程通信

    Java并发包——线程通信

    摘要:本文主要学习了Java并发包里有关线程通信的一些知识。

    部分内容来自以下博客:

    https://www.cnblogs.com/skywang12345/p/3496716.html

    线程通信方式

    对于线程之间的通信方式,我们之前使用Object.wait()和Object.notify(),通过与synchronized关键字进行同步,两者配合使用,可以实现线程之间的通信。

    后来在JUC并发包里发现可以使用Lock取代synchronized关键字实现线程之间的同步,并且使用Lock的方式有比synchronized方式更加强大的功能,为了同Lock配合,实现线程之间的通信,就要用到Condition。

    Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait()、notify()、notifyAll()方法是和“同步锁”(synchronized关键字)捆绑使用的,而Condition是需要与“独享锁/共享锁”捆绑使用的。

    使用Condition的优势

    能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。

    能够在多个线程之间进行通信。Object的wait()、notify()、notifyAll()方法只能实现两个线程之间的通信,而Lock对象能通过newCondition()方法创建出无数的“条件”,通过这些条件,我们就能够成功地实现多线程之间的数据通信,对它们进行控制。

    Condition

    Condition是java.util.concurrent.locks包下的一个接口,提供了用来进行线程通信的方法。

     1 public interface Condition {
     2     // 使当前线程加入等待队列中并释放当锁,被通知、被中断时唤醒。
     3     void await() throws InterruptedException;
     4 
     5     // 同await()类似,只是该方法对中断不敏感,只有被通知时才被唤醒。
     6     void awaitUninterruptibly();
     7 
     8     // 同await()类似,如果在指定时间之内没有被通知或者被中断,该方法会返回false。
     9     boolean await(long time, TimeUnit unit) throws InterruptedException;
    10 
    11     // 当前线程进入等待状态,被通知、中断或者超时之后被唤醒。返回值就是表示剩余的时间,超时返回值是0或者负数。
    12     long awaitNanos(long nanosTimeout) throws InterruptedException;
    13 
    14     // 同awaitNanos(long nanosTimeout)类似,只是参数变成了指定日期。
    15     boolean awaitUntil(Date deadline) throws InterruptedException;
    16 
    17     // 唤醒一个在等待队列中的线程。
    18     void signal();
    19 
    20     // 唤醒所有在等待队列中的线程。
    21     void signalAll();
    22 }

    获取Lock上的特定Condition

    Condition实例实质上被绑定到一个锁上。一个锁内部可以有多个Condition,即有多路等待和通知。要为特定Lock实例获得Condition实例,请使用Lock的newCondition()方法。

    newCondition()返回用来与当前Lock实例一起使用的Condition实例。

    类似于Object.wait()和Object.notify()的功能,Object.wait()与Object.notify()需要结合synchronized使用。Condition需要结合ReentrantLock使用。

    使用Condition实现线程通信

    使用synchronized和Object类的方法

    使用synchronized和Object类的方法实现两个线程交替打印,代码如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         DemoThread demoThread = new DemoThread();
     4         Thread a = new Thread(demoThread, "线程A");
     5         Thread b = new Thread(demoThread, "线程B");
     6         a.start();
     7         b.start();
     8     }
     9 }
    10 
    11 class DemoThread implements Runnable {
    12     private Integer num = 1;
    13 
    14     @Override
    15     public void run() {
    16         synchronized (DemoThread.class) {
    17             while (num <= 10) {
    18                 DemoThread.class.notify();
    19                 System.out.println(Thread.currentThread().getName() + " >>> " + num++);
    20                 if (num <= 10) {
    21                     try {
    22                         DemoThread.class.wait();
    23                     } catch (InterruptedException e) {
    24                         e.printStackTrace();
    25                     }
    26                 }
    27             }
    28         }
    29     }
    30 }

    运行结果如下:

     1 线程A >>> 1
     2 线程B >>> 2
     3 线程A >>> 3
     4 线程B >>> 4
     5 线程A >>> 5
     6 线程B >>> 6
     7 线程A >>> 7
     8 线程B >>> 8
     9 线程A >>> 9
    10 线程B >>> 10

    使用Lock和Condition类的方法

    使用Lock和Condition类的方法实现两个线程交替打印,代码如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         DemoThread demoThread = new DemoThread();
     4         Thread a = new Thread(demoThread, "线程A");
     5         Thread b = new Thread(demoThread, "线程B");
     6         a.start();
     7         b.start();
     8     }
     9 }
    10 
    11 class DemoThread implements Runnable {
    12     private Integer num = 1;
    13     Lock lock = new ReentrantLock();
    14     Condition condition = lock.newCondition();
    15 
    16     @Override
    17     public void run() {
    18         lock.lock();
    19         try {
    20             while (num <= 10) {
    21                 condition.signal();
    22                 System.out.println(Thread.currentThread().getName() + " >>> " + num++);
    23                 if (num <= 10) {
    24                     condition.await();
    25                 }
    26             }
    27         } catch (Exception e) {
    28             e.printStackTrace();
    29         } finally {
    30             lock.unlock();
    31         }
    32     }
    33 }

    运行结果如下:

     1 线程A >>> 1
     2 线程B >>> 2
     3 线程A >>> 3
     4 线程B >>> 4
     5 线程A >>> 5
     6 线程B >>> 6
     7 线程A >>> 7
     8 线程B >>> 8
     9 线程A >>> 9
    10 线程B >>> 10

    使用Lock和Condition类的方法实现三个线程按顺序循环打印

    使用Lock和Condition类的方法实现三个线程按顺序打印,需要唤醒指定的线程,代码如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         DemoThread demoThread = new DemoThread();
     4         Thread a = new Thread(() -> demoThread.run1(), "线程1");
     5         Thread b = new Thread(() -> demoThread.run2(), "线程2");
     6         Thread c = new Thread(() -> demoThread.run3(), "线程3");
     7         a.start();
     8         b.start();
     9         c.start();
    10     }
    11 }
    12 
    13 class DemoThread {
    14     static private Integer num = 0;
    15     static Lock lock = new ReentrantLock();
    16     Condition c1 = lock.newCondition();
    17     Condition c2 = lock.newCondition();
    18     Condition c3 = lock.newCondition();
    19 
    20     public void run1() {
    21         try {
    22             Thread.sleep(10);
    23         } catch (InterruptedException e1) {
    24             e1.printStackTrace();
    25         }
    26         lock.lock();
    27         try {
    28             while (num < 10) {
    29                 for (int i = 0; i < 1 && num < 10; i++) {
    30                     System.out.println(Thread.currentThread().getName() + " >>> " + num);
    31                 }
    32                 num++;
    33                 c2.signal();
    34                 c1.await();
    35                 if (num >= 9) {
    36                     c3.signal();
    37                 }
    38             }
    39         } catch (Exception e) {
    40             e.printStackTrace();
    41         } finally {
    42             lock.unlock();
    43         }
    44     }
    45 
    46     public void run2() {
    47         try {
    48             Thread.sleep(20);
    49         } catch (InterruptedException e1) {
    50             e1.printStackTrace();
    51         }
    52         lock.lock();
    53         try {
    54             while (num < 10) {
    55                 for (int i = 0; i < 2 && num < 10; i++) {
    56                     System.out.println(Thread.currentThread().getName() + " >>> " + num);
    57                 }
    58                 num++;
    59                 c3.signal();
    60                 c2.await();
    61                 if (num >= 9) {
    62                     c1.signal();
    63                 }
    64             }
    65         } catch (Exception e) {
    66             e.printStackTrace();
    67         } finally {
    68             lock.unlock();
    69         }
    70     }
    71 
    72     public void run3() {
    73         try {
    74             Thread.sleep(30);
    75         } catch (InterruptedException e1) {
    76             e1.printStackTrace();
    77         }
    78         lock.lock();
    79         try {
    80             while (num < 10) {
    81                 for (int i = 0; i < 3 && num < 10; i++) {
    82                     System.out.println(Thread.currentThread().getName() + " >>> " + num);
    83                 }
    84                 num++;
    85                 c1.signal();
    86                 c3.await();
    87                 if (num >= 9) {
    88                     c2.signal();
    89                 }
    90             }
    91         } catch (Exception e) {
    92             e.printStackTrace();
    93         } finally {
    94             lock.unlock();
    95         }
    96     }
    97 }

    运行结果如下:

     1 线程1 >>> 0
     2 线程2 >>> 1
     3 线程2 >>> 1
     4 线程3 >>> 2
     5 线程3 >>> 2
     6 线程3 >>> 2
     7 线程1 >>> 3
     8 线程2 >>> 4
     9 线程2 >>> 4
    10 线程3 >>> 5
    11 线程3 >>> 5
    12 线程3 >>> 5
    13 线程1 >>> 6
    14 线程2 >>> 7
    15 线程2 >>> 7
    16 线程3 >>> 8
    17 线程3 >>> 8
    18 线程3 >>> 8
    19 线程1 >>> 9

    Condition中的await()、signal()、signalAll()与Object中的wait()、notify()、notifyAll()区别

    使用方式不同

    Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。

    不同的是,Object中的这些方法是和同步锁捆绑使用的,而Condition是需要与互斥锁/共享锁捆绑使用的。

    功能更加强大

    Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。

    例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒“读线程”。当从缓冲区读出数据之后,唤醒“写线程”。当缓冲区满的时候,“写线程”需要等待。当缓冲区为空时,“读线程”需要等待。

    如果采用Object类中的wait()、notify()、notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒“读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。

    但是,通过Condition,就能明确的指定唤醒读线程。

  • 相关阅读:
    软件工程专业必须要会的算法
    软工实践寒假作业(1/2)
    我的思维导图
    个人简历
    当初希望自己是如何投入这个专业的学习的?曾经做过什么准备,或者立下过什么FLAG吗?
    当初对"软件工程"这个专业的期待和想象是什么?
    洛谷 题解 P1736 【创意吃鱼法】
    2018NOIP赛后总结+后阶段信奥学习个人规划
    洛谷 题解 P5015 【标题统计】 NOIP2018 普及组 T1
    NOIp考前注意事项
  • 原文地址:https://www.cnblogs.com/shamao/p/11027759.html
Copyright © 2011-2022 走看看