生产者消费者模型

有多个生产者线程和多个消费者线程, 中间有一个缓存区,缓存区满了之后,生产者线程阻塞,缓冲区为空时,消费者线程阻塞。

 

通过synchronized和wait/ notify来实现

  1)更加深刻理解了: 多个线程在并发的时候,通过synchronized加锁,那么在同一时刻只有一个线程来执行同步方法或者同步块中的代码(最多只有一个!!!)

  2)wait 与 notify的使用一定要放在synchronized方法或者同步快中,而且 调用wait / notify的对象与synchronized锁的对象是同一个,否则报,IllegalMonitorStateException异常(也是 RuntimeException 运行时异常的子类)

  3)我还曾试图将 缓存区满时 和 缓存区空时,分别用不同的锁对象,发现根本就没法实现(或许这就是ReentrantLock的一个意义把)

  4)在同步代码块中,if语句不好使,要用 while语句来进行判断。也就是要将wait 或者 await放到while 语句中,而不是if语句中。

  count++ 和 count-- 在底层包含三个操作,   1)获取count 的值  2)将count的值+1或者-1     3)将结果赋给count

    所以在并发的情况下,对count++或者count-- 要进行上锁,让其成为临界资源! 或者用 AtomicInteger 原子操作类

public class Test {

    public static void main(String[] args) {
        Demo demo=new Demo();
        // 生产者线程
        for(int i=0;i<5;i++){
            new Thread(demo.new ProducerThread(),"producer"+i).start();
        }
        // 消费者线程
        for(int i=0;i<5;i++){
            new Thread(demo.new ConsumerThread(),"consumer"+i).start();
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
class Demo{
    int count=5;  // 缓存区容量为5
    String full="a",empty="b";
    // 生产
    void produce() {
        // 同步加在这里吗?
        synchronized (this) {
            // 如果缓存满了  生产者阻塞
        //    if (count == 5) {
            while(count==5){    //  如果用if ,那么唤醒后,获取到锁后,就会直接往下执行,不会进行判断了,显然不合适,会造成超过缓存区的容量
                try {
                   // full.wait();
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count++;
            System.out.println(Thread.currentThread().getName()+":"+count);
            this.notify();
        }
    }
    //消费
    void consume(){

        // 如果缓存空了  消费者阻塞
       // synchronized (empty){
        synchronized (this){
          //  if(count==0){
            while (count==0){
                try {
                   // empty.wait();
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count--;
      //      full.notify();
            System.out.println(Thread.currentThread().getName()+":"+count);
            this.notify();
        }
    }

     class ProducerThread implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while(true){
                produce();
            }
        }
    }
    class ConsumerThread implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while(true){
                consume();
            }
        }
    }
}

通过ReentrantLock和 Condition来实现

  ReentrantLock的不同之处就是加了两个不同的锁,提高了并发效率。

  错了,也是得用同一把锁!!!! 用不同的锁,也会报 IllegalMonitorStateException 异常,就是加的锁和使用的 Condition的实例必须是对应的。但是可以用不同的Condition, 当然这些Condition是通过同一把锁来得到的。

  思考一下:ReentrantLock是怎么加锁的?  调用 lock.lock() 方法,底层通过AQS来判断资源是否被占有,没有被占有就继续向下执行,也就是执行try中的代码,如果被占有,就挂起当前线程。(调用LockSupport,park )

public class Test {

    public static void main(String[] args) {
        Demo demo=new Demo();
        // 生产者线程
        for(int i=0;i<5;i++){
            new Thread(demo.new ProducerThread(),"producer"+i).start();
        }
        // 消费者线程
        for(int i=0;i<5;i++){
            new Thread(demo.new ConsumerThread(),"consumer"+i).start();
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
class Demo{
    int count=5;  // 缓存区容量为5
    ReentrantLock lock1=new ReentrantLock();
  //  ReentrantLock lock2=new ReentrantLock();
    Condition condition1=lock1.newCondition();
    Condition condition2=lock1.newCondition();
    String full="a",empty="b";
    // 生产
    void produce() {
        // 同步加在这里吗?
        lock1.lock();
        try {
            while(count == 5) {
                try {
                    condition1.await();  //  会释放资源吗? 会,不用while 结果也是会出错!!!!
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count++;
            condition2.signal();
            System.out.println(Thread.currentThread().getName() + ":" + count);
        }finally {
            lock1.unlock();
        }

    }
    //消费
    void consume(){
       lock1.lock();
       try {
           while(count==0) {
               try {
                   condition2.await();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           count--;
           condition1.signal();
           System.out.println(Thread.currentThread().getName()+":"+count);
       }finally {
           lock1.unlock();
       }
    }

     class ProducerThread implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while(true){
                produce();
            }
        }
    }
    class ConsumerThread implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            while(true){
                consume();
            }
        }
    }
}

 

通过阻塞队列来实现

 

posted @ 2020-07-18 17:49  你眼里的星辰  阅读(114)  评论(0)    收藏  举报