使用wait()和notify()方法实现生产者消费者模式

以下总结只是个人理解,如有错误,敬请指正。

一、wait()和notify()作用

wait()和notify()方法是Object类的方法,因为Object类是所有类的根类,因此所有类都有这两个方法。

当调用对象的wait()方法时会释放获取的该对象的锁。

既然是释放锁,我们首先要有锁才能释放,因此wait()方法必须在同步方法或同步代码块中才能执行即在synchronized修饰的方法或代码块。

当调用对象的notify()方法时会唤醒等待获取该对象锁的一个线程,还有一个方法notifyAll()是唤醒等待获取该对象锁的所有线程。

同理要唤醒其他等待获取对象锁的线程,必须先拥有该对象锁,因为如果锁都不在自己手里,唤醒其他等待该对象锁的线程又有什么意义,自己又没有能力把锁释放掉交给别人。因此,notify()方法也需要在synchronized()代码块中执行。

二、synchronized关键字作用

既然wait()和notify()方法都涉及到synchronized关键字,我们就来看看这个关键字的作用。

synchronized关键字的作用是在被修饰的方法或代码块的开始和结束分别加上monitorenter和monitorexit字节码指令,当执行monitorenter指令时对象头中的一个计数器会加一,执行monitorenter指令时,对象头中的计数器会减一。需要注意的是该锁是可重入锁,即如果已经获取到了对象锁,可以重复获取,但monitorenter和monitorexit必须保持一一对应。

synchronized关键字用于修饰方法或者代码块,修饰方法时整个方法都被同步,同一时间只能有一个线程可以访问,修饰代码块时同理同一时间只能一个线程可以访问,但是代码块所在的方法里的其他部分仍然是同时可以被不同线程访问。

synchronized可以用于修饰普通方法和静态方法,修饰普通方法时获取的就是对象的锁,修饰静态方法时获取的就是实例化对象的类的Class对象的锁。

三、生产者消费者模式

生产者消费者模式顾名思义就是生产者不断生产产品,消费者不断消费产品,通常生产的产品会有一定的数量限制,如果生产的产品数量达到上限,必须停止生产,直到产品数量小于最大限制时才可以继续生产。如果没有任何产品可以消费,消费者必须等待直到有产品才可以继续消费。通过代码表达就是有两个线程,一个固定大小的队列。一个线程负责向队列中插入数据,另一个线程从线程中获取数据。为了达到上面的生产、消费条件需要通过wait()方法和notify()方法来实现。并且wait()和notify()方法的调用时机不同,可能会产生不同的结果,比如随机生产消费和生产一件产品消费一件产品等多种结果,下面看一个随机生产、消费的代码示例。

import java.util.PriorityQueue;

public class TestProducterConsumer
{
    private int queueSize=10;
    private PriorityQueue<Integer>queue=new PriorityQueue<Integer>(queueSize);
    
    public static void main(String[] args) throws InterruptedException
    {
        TestProducterConsumer test=new TestProducterConsumer();
        Thread producer=test.new Producter();
        Thread consumer=test.new Consumer();
        producer.start();
        consumer.start();
    }
    
    //生产者线程
    class Producter extends Thread{
        
        @Override
        public void run(){
            while (true)
            {
                //同步代码块,获取队列锁
                synchronized (queue)
                {
                    //当队列不满时生产者可以继续生产,生产之后唤醒消费者
                    //唤醒消费者,在生产者释放锁之后,消费者不一定就会获取锁,也许是生产者获取到锁继续执行
                    //但是如果不唤醒生产者,当队列满时,如果消费者处于阻塞状态,那么生产者和消费者都处于阻塞状态,程序就无法继续执行
                    if (queue.size()<queueSize)
                    {
                        queue.add(queue.size()+1);
                        System.out.println("生产者向队列中加入产品P,队列剩余空间:"+(queueSize-queue.size()));
                        try
                        {
                            //模拟生产者生产过程,sleep不会释放锁
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                        //唤醒消费者
                        queue.notify();//1)随机生产和消费
                    }else {
                        try
                        {
                            System.out.println("队列已满等待消费者消费");
                            //队列已满,进入阻塞状态,等待消费者消费
                            queue.wait();//1)随机生产和消费
                            //System.out.println("生产者获取到队列锁准备生产");
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                }
            }
            
        }
    }
    
    //消费者线程
    class Consumer extends Thread{
        @Override
        public void run(){
            while (true)
            {
                //同步代码块,获取队列锁
                synchronized (queue)
                {
                    //如果队列是空的,消费者进入阻塞状态,等待生产者生产并唤醒
                    if (queue.isEmpty())
                    {
                        System.out.println("没有产品可以消费,进入阻塞状态等待生产者生产。");
                        try
                        {
                            //进入阻塞状态释放队列锁,因为只有两个线程,所以生产者一定会获取到队列锁执行
                            queue.wait();//1)随机生产和消费
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                            //如果发送异常,主动唤醒生产者线程执行
                            queue.notify();
                        }
                        //System.out.println("消费者获取到队列锁准备消费");
                    }else {
                        //如果队列不空,就消费产品,并唤醒生产者
                        //注意唤醒生产者,在消费者执行完毕释放锁之后,不一定生产者就会获得锁,也许消费者会继续获取锁执行
                        //但是如果不唤醒生产者,那么如果生产者处于阻塞状态,当队列为空,消费者也进入阻塞状态那么就没有线程可以获取锁继续执行了
                        queue.notify();//1)随机生产和消费
                        try
                        {
                            //模拟消费者消费过程,sleep不会释放锁
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                        queue.poll();
                        System.out.println("消费者消费了产品P,剩余空间:"+(queueSize-queue.size()));
                    }
                }
            }
        }
    }
}

执行结果:

下面是生产者消费者轮流执行的代码示例,和随机执行的区别就在于,在生产者生产之后唤醒消费者并立刻进入阻塞状态释放锁,这样消费者就可以获取到锁执行。在消费者消费之后唤醒生产者并立刻进入阻塞状态释放锁,这样消费者就可以获取到锁执行。

import java.util.PriorityQueue;

public class TestProducterConsumer
{
    private int queueSize=10;
    private PriorityQueue<Integer>queue=new PriorityQueue<Integer>(queueSize);
    
    public static void main(String[] args) throws InterruptedException
    {
        TestProducterConsumer test=new TestProducterConsumer();
        Thread producer=test.new Producter();
        Thread consumer=test.new Consumer();
        producer.start();
        Thread.sleep(1000);
        consumer.start();
    }
    
    //生产者线程
    class Producter extends Thread{
        
        @Override
        public void run(){
            while (true)
            {
                //同步代码块,获取队列锁
                synchronized (queue)
                {
                    //当队列不满时生产者可以继续生产,生产之后唤醒消费者
                    //唤醒消费者,在生产者释放锁之后,消费者不一定就会获取锁,也许是生产者获取到锁继续执行
                    //但是如果不唤醒生产者,当队列满时,如果消费者处于阻塞状态,那么生产者和消费者都处于阻塞状态,程序就无法继续执行
                    if (queue.size()<queueSize)
                    {
                        queue.add(queue.size()+1);
                        System.out.println("生产者向队列中加入产品P,队列剩余空间:"+(queueSize-queue.size()));
                        try
                        {
                            //模拟生产者生产过程,sleep不会释放锁
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                        //唤醒消费者
                        queue.notify();//1)随机生产和消费
                        try
                        {
                            //生产之后进入立刻阻塞状态,让消费者消费
                            queue.wait();
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }else {
                        try
                        {
                            System.out.println("队列已满等待消费者消费");
                            //队列已满,进入阻塞状态,等待消费者消费
                            queue.wait();//1)随机生产和消费
                            //System.out.println("生产者获取到队列锁准备生产");
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
            
        }
    }
    
    //消费者线程
    class Consumer extends Thread{
        @Override
        public void run(){
            while (true)
            {
                //同步代码块,获取队列锁
                synchronized (queue)
                {
                    //如果队列是空的,消费者进入阻塞状态,等待生产者生产并唤醒
                    if (queue.isEmpty())
                    {
                        System.out.println("没有产品可以消费,进入阻塞状态等待生产者生产。");
                        try
                        {
                            //进入阻塞状态释放队列锁,因为只有两个线程,所以生产者一定会获取到队列锁执行
                            queue.wait();//1)随机生产和消费
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                            //如果发送异常,主动唤醒生产者线程执行
                            queue.notify();
                        }
                        //System.out.println("消费者获取到队列锁准备消费");
                    }else {
                        //如果队列不空,就消费产品,并唤醒生产者
                        //注意唤醒生产者,在消费者执行完毕释放锁之后,不一定生产者就会获得锁,也许消费者会继续获取锁执行
                        //但是如果不唤醒生产者,那么如果生产者处于阻塞状态,当队列为空,消费者也进入阻塞状态那么就没有线程可以获取锁继续执行了
                        queue.notify();//1)随机生产和消费
                        try
                        {
                            //模拟消费者消费过程,sleep不会释放锁
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                        queue.poll();
                        System.out.println("消费者消费了产品P,剩余空间:"+(queueSize-queue.size()));
                        try
                        {
                            //消费之后立刻进入阻塞状态,让生产者生产
                            queue.wait();
                        }
                        catch (InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的使用wait()和notify()实现的生产者-消费者模型: ``` java public class ProducerConsumer { private List<Integer> buffer = new ArrayList<>(); private int maxSize = 5; public synchronized void produce() throws InterruptedException { while (buffer.size() == maxSize) { wait(); } int item = new Random().nextInt(); buffer.add(item); System.out.println("Produced: " + item); notify(); } public synchronized void consume() throws InterruptedException { while (buffer.size() == 0) { wait(); } int item = buffer.remove(0); System.out.println("Consumed: " + item); notify(); } } ``` 在上述代码中,`buffer`是一个用来存储生产者生产的数据的缓冲区,`maxSize`是缓冲区的最大大小,`produce()`和`consume()`分别表示生产者和消费者的行为。在`produce()`和`consume()`方法中,使用了`wait()`和`notify()`方法实现线程的等待和唤醒。 在生产者线程中,如果缓冲区已满,就让线程等待(调用`wait()`方法)。如果缓冲区不满,就生产一个随机数,并且将其加入缓冲区中,然后唤醒等待的消费者线程(调用`notify()`方法)。 在消费者线程中,如果缓冲区为空,就让线程等待(调用`wait()`方法)。如果缓冲区不为空,就从缓冲区中取出一个数据,并且将其打印出来,然后唤醒等待的生产者线程(调用`notify()`方法)。 注意,以上代码仅作为示例,是一个比较简单的实现。在实际生产环境中,可能需要更加完善的实现方式来保证线程安全和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值