• Java多线程之线程间的通信


    1.线程间的通信

    • 线程间通信

      • 生产者+消费者
      • 通知等待唤醒机制
    • 多线程编程模板

      • 判断 干活 通知
      • 判断需使用while,以防止中断和虚假唤醒(见java.lang.Object的API)

      A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one

         synchronized (obj) {
            	while (<condition does not hold>)
            		obj.wait(timeout);
            	... // Perform action appropriate to condition
         }
      

      线程也可以在没有通知、中断或超时的情况下被唤醒,这就是所谓的假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试导致线程被唤醒的条件来防止这种情况发生,如果条件不满足,则继续等待。换句话说,等待应该总是出现在循环中,就像这个循环一样

    1.1 synchronized版

    • synchronized实现

      • 先2个线程操作资源类,资源中的操作判断使用if,如线程A和线程B,可以正常运行1 0 1 0 1 0...
      • 增加2个线程C和D,模拟虚假唤醒,判断依旧是if,运行的结果数字不是全部为1、0
        • 原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次
      • 在4线程中,将资源类的if判断改为while判断,while是只要唤醒就要拉回来再判断一次
      package juc.demo;
      
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      /**
       * @Description:
       *  现在两个线程,
       *  可以操作初始值为零的一个变量,
       *  实现一个线程对该变量加1,一个线程对该变量减1,
       *  交替,来10轮。
       * @Package: juc.demo
       * @ClassName NotifyWaitDemo
       * @author: wuwb
       * @date: 2020/10/19 13:30
       */
      public class NotifyWaitDemo {
          public static void main(String[] args) {
              int turn = 1000;
              //资源类
              ShareData data = new ShareData();
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.increment();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "A").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.decrement();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "B").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.increment();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "C").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.decrement();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "D").start();
          }
      }
      
      //资源类
      class ShareData{
          private int number = 0;
      
          public synchronized void increment() throws InterruptedException {
              //判断  if换while
              while (number != 0) {
                  this.wait();
              }
              //干活
              number++;
              System.out.println(Thread.currentThread().getName() + ":" + number);
              //通知
              this.notifyAll();
          }
      
          public synchronized void decrement() throws InterruptedException {
              while (number == 0) {
                  this.wait();
              }
              number--;
              System.out.println(Thread.currentThread().getName() + ":" + number);
              this.notifyAll();
          }
      
      }
      
      

    2.2 JUC版

    • Lock 及 Condition

      package juc.demo;
      
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      /**
       * @Description:
       *  现在两个线程,
       *  可以操作初始值为零的一个变量,
       *  实现一个线程对该变量加1,一个线程对该变量减1,
       *  交替,来10轮。
       * @Package: juc.demo
       * @ClassName NotifyWaitDemo
       * @author: wuwb
       * @date: 2020/10/19 13:30
       */
      public class NotifyWaitDemo {
          public static void main(String[] args) {
              int turn = 1000;
              //资源类
              ShareData data = new ShareData();
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.increment();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "A").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.decrement();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "B").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.increment();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "C").start();
      
              new Thread(() -> {
                  for (int i = 0; i < turn; i++) {
                      try {
                          data.decrement();
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }, "D").start();
          }
      }
      
      //资源类
      class ShareData{
          private int number = 0;
          private Lock lock = new ReentrantLock();
          private Condition condition = lock.newCondition();
      
          public void increment() {
              lock.lock();
              try {
                  //判断
                  while (number != 0) {
                      condition.await();//this.wait();
                  }
                  //干活
                  number++;
                  System.out.println(Thread.currentThread().getName() + ":" + number);
                  //通知
                  condition.signalAll();//this.notifyAll();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
          }
      
          public void decrement() {
              lock.lock();
              try {
                  while (number == 0) {
                      condition.await();
                  }
                  number--;
                  System.out.println(Thread.currentThread().getName() + ":" + number);
                  condition.signalAll();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
          }
      
      }
      
      

    3.3 定制化调用通信

    • 使用Lock、Condition指定调用顺序,指定唤醒哪个线程
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Description
     * 多线程之间按顺序调用,实现A->B->C
     * ......来10轮
     */
    public class ThreadOrderAccess {
        public static void main(String[] args) {
            ShareResource resource = new ShareResource();
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    resource.printA();
                }
            }, "A").start();
    
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    resource.printB();
                }
            }, "B").start();
    
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    resource.printC();
                }
            }, "C").start();
        }
    }
    
    class ShareResource{
        /**标志位*/
        private int number = 1;
        private Lock lock = new ReentrantLock();
        /**3把钥匙*/
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
       
        public void printA() {
            lock.lock();
            try {
                while (number != 1) {
                    condition1.await();
                }
                System.out.println(Thread.currentThread().getName()+"==>AAAAAAAAAA");
                number = 2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void printB() {
            lock.lock();
            try {
                while (number != 2) {
                    condition2.await();
                }
                System.out.println(Thread.currentThread().getName()+"==>BBBBBBBBBB");
                number = 3;
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void printC() {
            lock.lock();
            try {
                while (number != 3) {
                    condition3.await();
                }
                System.out.println(Thread.currentThread().getName()+"==>CCCCCCCCCC");
                number = 1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
  • 相关阅读:
    关于jQuery方法解析(一)append-参数设置问题
    CSS动画
    关于html CSS 绝对相对布局问题
    Chrome自带的开发者工具使用方法教程
    web常见漏洞及防范方法
    前端性能优化 Web前端应该从哪些方面来优化网站?
    属性的特征描述可以分为两类:数据属性和访问器属性
    iScroll.js的用法
    百度前端学院在线学习参考资料
    GET和POST的区别,何时使用POST?
  • 原文地址:https://www.cnblogs.com/wuweibincqu/p/14139272.html
Copyright © 2020-2023  润新知