JAVA并发编程:阻塞队列-ArrayBlockingQueue

生活

有很多的不快乐,其实是源自不满足,而不满足,很多时候是源自于心不定,而心不定则是因为不清楚究竟自己要什么,不清楚要什么的结果就是什么都想要,结果什么都没得到。

生产者消费者模式

生产者和消费者问题是线程模型中一个经典问题:
生产者和消费者在同一个时间段内共用一块内存区域,由生产者在这块内存区域创建消费者需要的数据,由消费者取走数据并消费。

对于生产者消费者模型的应用实例,JDK1.5提供了阻塞队列。
下面来看下ArrayBlockingQueue,这是一个有界的,数组结构的阻塞队列。

ArrayBlockingQueue 成员组成

先来看下ArrayBlockingQueue的成员组成:

	//元素容器
    final Object[] items;
	//出队索引
    int takeIndex;
	//入队索引
    int putIndex;
    //队列中元素个数
    int count;
    //锁
    final ReentrantLock lock;
	//出队条件
    private final Condition notEmpty;
	//入队条件
    private final Condition notFull;

组成明了,出队和入队各有一个条件,互不干扰。

ArrayBlockingQueue 之创建

//创建指定长度的非公平 有界数组阻塞队列
   public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }


//可以指定fair为true创建公平的队列
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

//可以传入集合 构建一个初始化队列
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

ArrayBlockingQueue 之生产

不阻塞生产:

//调用offer,如果满了就报错
    public boolean add(E e) {
        return super.add(e);
    }
    //
    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
           //满了就返回false;
            if (count == items.length)
                return false;
            else {
                insert(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

阻塞生产:

//阻塞一定时间,不能入队就返回false,可以中断
 public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            insert(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

// 一直阻塞直到入队或者被中断
 public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
        //当队列满时等待
            while (count == items.length)
                notFull.await();
            insert(e);
        } finally {
            lock.unlock();
        }
    }

核心入队方法:

private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        //入队成功后唤醒等到在NotEmpty上的线程
        notEmpty.signal();
    }

ArrayBlockingQueue 之消费

不阻塞消费:

//  没有元素返回null,有就返回并移除队列中该元素
 public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : extract();
        } finally {
            lock.unlock();
        }
    }

 

//不阻塞返回数据 不移除队列
public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : itemAt(takeIndex);
        } finally {
            lock.unlock();
        }
    }

阻塞消费:

//一直阻塞值到出队 或者被中断
public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return extract();
        } finally {
            lock.unlock();
        }
    }
    //阻塞消费,超时后返回null,可以中断
 public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return extract();
        } finally {
            lock.unlock();
        }
    }
    

出队核心方法:

 private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        //移除元素后并没有整体往前挪,只是索引+1
        takeIndex = inc(takeIndex);
        --count;
        //出队成功后唤醒等待在NotFull上的线程
        notFull.signal();
        return x;
    }

实例

public class ABQTest {

    public static class Bread{
        String name;

        String price;

        public Bread(String name, String price) {
            this.name = name;
            this.price = price;
        }

        @Override
        public String toString() {
            return String.format("[面包:%s,价格:%s]",name,price);
        }
    }


    public static class Producer implements  Runnable{
        private ArrayBlockingQueue<Bread> queue;
        private Bread bread;
        private String name;
        private CountDownLatch latch;


        @Override
        public void run() {

            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                Thread.sleep(Long.valueOf(new Random().nextInt(1000)));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                queue.put(bread);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(String.format("name:%s,生产:%s",name,bread.toString()));
        }

        public Producer(ArrayBlockingQueue<Bread> queue, Bread bread, String name, CountDownLatch latch) {
            this.queue = queue;
            this.bread = bread;
            this.name = name;
            this.latch = latch;
        }
    }

    public static class Consumer implements  Runnable{
        private ArrayBlockingQueue<Bread> queue;
        private String name;
        private CountDownLatch latch;


        @Override
        public void run() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(Long.valueOf(new Random().nextInt(1000)));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Bread bread = null;
            try {
                bread=queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(String.format("name:%s,买了:%s",name,bread.toString()));
        }

        public Consumer(ArrayBlockingQueue<Bread> queue, String name, CountDownLatch latch) {
            this.queue = queue;
            this.name = name;
            this.latch = latch;
        }
    }

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(1);
        ArrayBlockingQueue<Bread> queue = new ArrayBlockingQueue<Bread>(4);
        new Thread(new Producer(queue,new Bread("肉松","10"),"赵",latch)).start();
        new Thread(new Producer(queue,new Bread("蛋黄","7"),"钱",latch)).start();
        new Thread(new Producer(queue,new Bread("火腿","15"),"孙",latch)).start();
        new Thread(new Producer(queue,new Bread("蔬菜","5"),"李",latch)).start();
        new Thread(new Producer(queue,new Bread("香肠","8"),"周",latch)).start();
        new Thread(new Consumer(queue,"A",latch)).start();
        new Thread(new Consumer(queue,"B",latch)).start();
        new Thread(new Consumer(queue,"C",latch)).start();
        new Thread(new Consumer(queue,"D",latch)).start();
        new Thread(new Consumer(queue,"E",latch)).start();

        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();

    }

}

name:钱,生产:[面包:蛋黄,价格:7]
name:周,生产:[面包:香肠,价格:8]
name:A,买了:[面包:蛋黄,价格:7]
name:B,买了:[面包:香肠,价格:8]
name:赵,生产:[面包:肉松,价格:10]
name:E,买了:[面包:肉松,价格:10]
name:孙,生产:[面包:火腿,价格:15]
name:C,买了:[面包:火腿,价格:15]
name:D,买了:[面包:蔬菜,价格:5]
name:李,生产:[面包:蔬菜,价格:5]

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84647341