Java多線程(九):生產者消費者模型

生產者消費者模型

生產者:生產任務的個體;
消費者:消費任務的個體;
緩衝區:是生產者和消費者之間的媒介,對生產者和消費者解耦。

緩衝區元素爲滿,生產者沒法生產,消費者繼續消費;
緩衝區元素爲空,消費者沒法消費,生產者繼續生產;java

wait()/notify()生產者消費者模型

製做一個簡單的緩衝區ValueObject,value爲空表示緩衝區爲空,value不爲空表示緩衝區滿this

public class ValueObject {

    public static String value = "";

}

生產者,緩衝區滿則wait(),再也不生產,等待消費者notify(),緩衝區爲空則開始生產線程

public class Producer {
    private Object lock;

    public Producer(Object lock)
    {
        this.lock = lock;
    }

    public void setValue()
    {
        try
        {
            synchronized (lock)
            {
                if (!ValueObject.value.equals(""))
                    lock.wait();
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("Set的值是:" + value);
                ValueObject.value = value;
                lock.notify();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

消費者,緩衝區爲空則wait(),等待生產者notify(),緩衝區爲滿,消費者開始消費code

public class Customer {
    private Object lock;

    public Customer(Object lock)
    {
        this.lock = lock;
    }

    public void getValue()
    {
        try
        {
            synchronized (lock)
            {
                if (ValueObject.value.equals(""))
                    lock.wait();
                System.out.println("Get的值是:" + ValueObject.value);
                ValueObject.value = "";
                lock.notify();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

main方法,啓動一個生產者和一個消費者繼承

public class Main {
    public static void main(String[] args)
    {
        Object lock = new Object();
        final Producer producer = new Producer(lock);
        final Customer customer = new Customer(lock);
        Runnable producerRunnable = new Runnable()
        {
            public void run()
            {
                while (true)
                {
                    producer.setValue();
                }
            }
        };
        Runnable customerRunnable = new Runnable()
        {
            public void run()
            {
                while (true)
                {
                    customer.getValue();
                }
            }
        };
        Thread producerThread = new Thread(producerRunnable);
        Thread CustomerThread = new Thread(customerRunnable);
        producerThread.start();
        CustomerThread.start();
    }
}

運行結果以下字符串

Set的值是:1564733938518_27520480474279
Get的值是:1564733938518_27520480474279
Set的值是:1564733938518_27520480498378
Get的值是:1564733938518_27520480498378
Set的值是:1564733938518_27520480540254
Get的值是:1564733938518_27520480540254
······

生產者和消費者交替運行,生產者生產一個字符串,緩衝區爲滿,消費者消費一個字符串,緩衝區爲空,循環往復,知足生產者/消費者模型。get

await()/signal()生產者/消費者模型

緩衝區it

public class ValueObject {

    public static String value = "";

}

ThreadDomain48繼承ReentrantLock,set方法生產,get方法消費io

public class ThreadDomain48 extends ReentrantLock
{
    private Condition condition = newCondition();

    public void set()
    {
        try
        {
            lock();
            while (!"".equals(ValueObject.value))
                condition.await();
            ValueObject.value = "123";
            System.out.println(Thread.currentThread().getName() + "生產了value, value的當前值是" + ValueObject.value);
            condition.signal();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }

    public void get()
    {
        try
        {
            lock();
            while ("".equals(ValueObject.value))
                condition.await();
            ValueObject.value = "";
            System.out.println(Thread.currentThread().getName() + "消費了value, value的當前值是" + ValueObject.value);
            condition.signal();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        finally
        {
            unlock();
        }
    }
}

MyThread41啓動兩個生產線程和一個消費線程class

public class MyThread41 {
    public static void main(String[] args)
    {
        final ThreadDomain48 td = new ThreadDomain48();
        Runnable producerRunnable = new Runnable()
        {
            public void run()
            {
                for (int i = 0; i < Integer.MAX_VALUE; i++)
                    td.set();
            }
        };
        Runnable customerRunnable = new Runnable()
        {
            public void run()
            {
                for (int i = 0; i < Integer.MAX_VALUE; i++)
                    td.get();
            }
        };
        Thread ProducerThread1 = new Thread(producerRunnable);
        ProducerThread1.setName("Producer1");
        Thread ProducerThread2 = new Thread(producerRunnable);
        ProducerThread2.setName("Producer2");
        Thread ConsumerThread = new Thread(customerRunnable);
        ConsumerThread.setName("Consumer");
        ProducerThread1.start();
        ProducerThread2.start();
        ConsumerThread.start();
    }
}

輸出結果以下

Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123

爲何Producer2沒法生產,消費者沒法消費呢?是由於此時緩衝區爲滿,Producer1的notify()應該喚醒Consumer卻喚醒了Producer2,致使Producer2由於緩衝區爲滿和Consumer沒有被喚醒而處於waiting狀態,此時三個線程均在等待,出現了假死。
解決方案有兩種:
1.讓生產者喚醒全部線程,在set方法中使用condition.signalAll();
2.使用兩個Condition,生產者Condition和消費者Condition,喚醒指定的線程;
正常輸入以下:

······
Producer2生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer2生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer2生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
Producer1生產了value, value的當前值是123
Consumer消費了value, value的當前值是
······
相關文章
相關標籤/搜索