生产者消费者模式的java实现(实现二)

这次采用ReentrantLock 实现生产者消费者模式,先说下ReentrantLock,通常叫做重入锁,所谓重入就是一个线程可以再次进入已经持有锁的代码块,在内部会对重入的次数进行计数,当计数为0,则释放锁。其实synchronized关键字所代表的内置锁,也是可以重入的。但是又有几点不同:

1、ReentrantLock将加锁与解锁进行分离,可以提供更细粒度的加解锁操作,内置锁基本都是全局锁(类,对象,代码块)

2、ReentrantLock提供了定时的加锁操作,这是内置锁无法做到的。

3、ReentrantLock提供了可轮询的加锁操作,即tryLock()方法,在循环中使用,可以减少线程争抢,减少系统调用和上下文切换,节省资源,这也是内置锁无法做到的。

package com.smikevon.concurrent;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class ProducerConsumer_05 {

    public static void main(String[] args) {

        final Drop drop = new Drop();

        new Thread(new Runnable() {
            public void run() {
                try {
                    new Producer(drop).produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者").start();

        new Thread(new Runnable() {
            public void run() {
                try {
                    new Consumer(drop).consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者").start();
    }

    static class Producer{
        private Drop drop;
        public Producer(Drop drop) {
            this.drop = drop;
        }

        public void produce() throws InterruptedException{
            String[] messages = {
                    "我是",
                    "一名程序员",
                    "我很骄傲",
                    "也很自豪",
                    "爱岗敬业",
                    "勤劳奉献",
                    "无怨无悔",
                    "奉献青春",
                    "希望通过学习",
                    "提升",
                    "自己",
                    "done",
                };
            for(int i=0;i<messages.length;i++){
                drop.put(messages[i]);

            }

        }
    }

    static class Consumer{

        private Drop drop;
        public Consumer(Drop drop) {
            this.drop = drop;
        }

        public void consume() throws InterruptedException{
            String message = "";
            do{
                message = drop.take();
            }while(!message.equals("done"));
        }
    }

    static class Drop{
        private Lock lock = new ReentrantLock();
        //初始化条件为空
        private Condition empty = lock.newCondition();
        //条件为满,这里有一个就是满了
        private Condition full = lock.newCondition();
        private String message;
        private boolean isExist = false ;

        public void put(String message) throws InterruptedException{
            while(true){
                try{
                    lock.lock();
                    if(isExist){
                        full.await();
                    }
                    this.message = message;
                    isExist = true;
                    empty.signal();
                    System.out.println(Thread.currentThread().getName()+":"+message);
                    break;
                }finally{
                    lock.unlock();
                }
            }
        }

        public String take() throws InterruptedException{
            while(true){
                try{
                    lock.lock();
                    if(!isExist){
                        empty.await();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+message);
                    isExist = false;
                    full.signal();
                    return this.message;

                }finally{
                    lock.unlock();
                }
            }
        }
    }

}

  这里使用了两个condition,一个表示是否满,一个表示是否空,赋予实际意义,如果有N个线程,定义N个condition条件。这样,在调用signal() 方法时,就是明确告诉该哪个线程从waiting状态中苏醒,恢复runnable状态。减少了内置锁notifyAll()带来的线程争抢锁,争抢时带来的系统调用和上线文切换非常消耗系统资源。

  第一篇和第二篇都是采用原始的锁工具实现生产者消费者模式,实际上java.util.concurrent包提供了一些方便的并发工具,这些工具能够非常方便的实现生产者消费者模式。例如Exchanger,semaphore等。

原文地址:https://www.cnblogs.com/seanvon/p/4071841.html