Java multi-threaded classic-producer and consumer

1. notify与wait

Notify unlocks the thread, wait locks the thread.

Use the notify and wait methods to synchronize the threads, otherwise an error will be reported.

Notify and wait are dependent on the synchronization lock, so the lock in synchronized must be filled in the notify and wait thread.

Next, let’s talk about the use of wait() and notify() to achieve concurrency between producers and consumers:
 1. Obviously, it is necessary to ensure that producers and consumers run concurrently without chaos. The main solution is: when the buffer area of ​​the producer thread is When it is full, wait() should be called to stop the producer from continuing production, and when the producer's full buffer is consumed by the consumer, notify() should be called to wake up the producer and notify him that it can continue production; the same For consumers, when the buffer of the consumer thread is empty, wait() should be called to stop the consumer thread to continue consumption, and when the producer produces another one, it should call notify() to wake up the consumer The thread informs him that he can continue to consume.

What needs to be emphasized here is that the wait method and notify method are not methods on the Thread thread, they are methods on the Object.

Because all Objects can be used as synchronization objects, to be precise, wait and notify are methods on synchronization objects.

The meaning of wait()
 1. Let the thread occupying this synchronization object temporarily release the current occupation and wait. So there is a precondition to call wait, it must be in the synchronized block, otherwise an error will occur.

The meaning of notify()
 1. Notify a thread waiting on this synchronization object. You can wake up and have a chance to reoccupy the current object.

  Calling the notify() method of an object can wake up a thread that is waiting for the monitor (lock) of this object. If multiple threads are waiting for the monitor of this object, only one thread can be awakened;

  A thread being awakened does not mean that the monitor of the object is immediately acquired. Only after calling notify() or notifyAll() and exiting the synchronized block, after releasing the object lock, the remaining threads can obtain the lock for execution.

2. Demonstration of notify and wait program

public class Test {
    
    
  // 在多线程间共享的对象上使用wait
  private static String shareObj = "true";

  public static void main(String[] args) throws IOException, ClassNotFoundException {
    
    

    Thread1 a = new Thread1();
    a.setName("a");
    /* 启动线程a */
    a.start();

    Thread2 b = new Thread2();
    b.setName("b");
    /* 启动线程b */
    b.start();

  }

  static class Thread1 extends Thread {
    
    

    @Override
    public void run() {
    
    
      try {
    
    
        synchronized (shareObj) {
    
    
          System.out.println("开始等待线程获取锁");
          /* 等待获取锁,需要其他线程将对象锁进行释放 */
          shareObj.wait();
        }

      } catch (InterruptedException e) {
    
    
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      System.out.println(this.getName() + "线程,获取到锁");
    }
  }

  static class Thread2 extends Thread {
    
    
    @Override
    public void run() {
    
    
      try {
    
    
        synchronized (shareObj) {
    
    
          shareObj.notify();
          System.out.println("线程" + Thread.currentThread().getName() + "调用shareObj.notify()");

          /*
           * 只要当前线程等待2s后,线程Thread1才会被执行 因为只要执行完synchronized语句块,对象锁才会被释放
           * 当对象锁被释放的时候,线程Thread1才会执行,因为获得到锁
           */
          sleep(2000);
        }
      } catch (Exception e) {
    
    
        // TODO: handle exception
      }
      System.out.println(this.getName() + "释放了锁");
    }
  }
}

3. Producer and consumer procedures

package acc;

import java.util.Date;

public class Test {
    
    

  final static Account ac = new Account();

  /**
   * 多线程并发问题演示 当多个线程同时访问一个对象时就可能发生同步问题 如果想看bug,不要使用虚拟机,使用真实电脑测试
   * 
   * @param args
   */
  public static void main(String[] args) {
    
    

    ac.setN(1000);// 账户金额1000

    final Date dt = new Date();

    Thread xx = new Thread("张三") {
    
     // 连续存款50次,每次100
      @Override
      public void run() {
    
    

        try {
    
    

          for (int i = 1; i <= 50; i++) {
    
    
            System.out.println("");

            /**
             * 为代码段加锁,如果其他线程正在占用锁,这里就等待 此代码段开始之后就会占用这把锁,其他使用本锁的地方将会等待
             */
            synchronized (Test.class) {
    
    
              int n = ac.getN();// 获取余额
              System.out.println(this.getName() + " - 查询余额 :" + n);
              n = n + 100;

              sleep(1);
              ac.setN(n);
            }

            System.out.println(this.getName() + " - 存100查询 :" + ac.getN());
          }

        } catch (Exception e) {
    
    
          e.printStackTrace();
        }

        System.out.println(this.getName() + " -- OVER : " + ac.getN());
      }
    };

    xx.start();

    new Thread("李四") {
    
     // 连续存款50次,每次100
      @Override
      public void run() {
    
    

        try {
    
    

          for (int i = 1; i <= 50; i++) {
    
    
            System.out.println("");
            /**
             * 为代码段加锁,如果其他线程正在占用锁,这里就等待 此代码段开始之后就会占用这把锁,其他使用本锁的地方将会等待
             */
            synchronized (Test.class) {
    
    
              int n = ac.getN();// 获取余额
              System.out.println(this.getName() + " - 查询余额 :" + n);
              n = n - 100;

              sleep(1);
              ac.setN(n);

            }

            System.out.println(this.getName() + " - 取出100之后查询 :" + ac.getN());
          }

        } catch (Exception e) {
    
    
          e.printStackTrace();
        }

        System.out.println(this.getName() + " -- OVER : " + ac.getN());
      }
    }.start();

  }

}

Guess you like

Origin blog.csdn.net/weixin_43088443/article/details/112799813