java多线程经典——生产者和消费者

1. notify与wait

notify解锁线程,wait锁住线程。

使用notify和wait方法必须对线程进行同步,否则会报错。

notify和wait是依赖于同步锁,因此synchronized中的锁,必须填写notify和wait的线程。

接下来说说利用wait()和notify()来实现生产者和消费者并发问题:
 1. 显然要保证生产者和消费者并发运行不出乱,主要要解决:当生产者线程的缓存区为满的时候,就应该调用wait()来停止生产者继续生产,而当生产者满的缓冲区被消费者消费掉一块时,则应该调用notify()唤醒生产者,通知他可以继续生产;同样,对于消费者,当消费者线程的缓存区为空的时候,就应该调用wait()停掉消费者线程继续消费,而当生产者又生产了一个时就应该调用notify()来唤醒消费者线程通知他可以继续消费了。

这里需要强调的是,wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。

因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。

wait()的意思
 1. 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。

notify() 的意思
 1. 通知一个等待在这个同步对象上的线程,你可以苏醒过来了,有机会重新占用当前对象了。

  调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor(锁)的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

  1个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

2. notify和wait的程序演示

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. 生产者与消费者的程序

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();

  }

}

猜你喜欢

转载自blog.csdn.net/weixin_43088443/article/details/112799813