java ArrayBlockingQueue 阻塞队列实现的生产者消费者模式

1.ArrayBlockingQueue

ArrayBlockingQueue是一个用数组实现的有界阻塞列。此列按照先先出(FIFO)的原

则对元素行排序。

情况下不保证线程公平的访问队列,所公平访问队列是指阻塞的线程,可以按照

阻塞的先后访问队列,即先阻塞线程先访问队列。非公平性是先等待的线程是非公平

的,当列可用,阻塞的线程都可以争夺访问队列的格,有可能先阻塞的线程最后才访问

列。了保公平性,通常会降低吞吐量。我可以使用以下代码创建一个公平的阻塞

列。

ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,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();

}

2.LinkedBlockingQueue

LinkedBlockingQueue是一个用实现的有界阻塞列。此列的默和最大

Integer.MAX_VALUE。此列按照先先出的原则对元素行排序。

3.PriorityBlockingQueue

PriorityBlockingQueue是一个支持的无界阻塞列。默情况下元素采取自然

升序排列。也可以自定义类实现compareTo()方法来指定元素排序规则,或者初始化

PriorityBlockingQueue,指定构造参数Comparator元素行排序。需要注意的是不能保

元素的序。

4.DelayQueue

DelayQueue是一个支持延时获取元素的无界阻塞列。列使用PriorityQueue实现

列中的元素必须实现Delayed接口,在建元素可以指定多久才能从列中取当前元素。

只有在延满时才能从列中提取元素。

DelayQueue非常有用,可以将DelayQueue运用在以下景。

·存系设计:可以用DelayQueue保存存元素的有效期,使用一个线程循环查询

DelayQueue,一旦能从DelayQueue取元素,表示存有效期到了。·务调度:使用DelayQueue保存当天将会行的任时间,一旦从

DelayQueue取到任就开始行,比如TimerQueue就是使用DelayQueue实现的。

1)如何实现Delayed接口

DelayQueue列的元素必须实现Delayed接口。我可以参考ScheduledThreadPoolExecutor

ScheduledFutureTask实现,一共有三步。

第一步:在建的候,初始化基本数据。使用time记录当前象延到什么候可

以使用,使用sequenceNumber标识元素在列中的先后序。代如下。

private static final AtomicLong sequencer = new AtomicLong(0);

ScheduledFutureTask(Runnable r, V result, long ns, long period) {

ScheduledFutureTask(Runnable r, V result, long ns, long period) {

super(r, result);

this.time = ns;

this.period = period;

this.sequenceNumber = sequencer.getAndIncrement();

}

第二步:实现getDelay方法,方法返回当前元素需要延长时间位是秒,代

如下。

public long getDelay(TimeUnit unit) {

return unit.convert(time - now(), TimeUnit.NANOSECONDS);

}

构造函数可以看出延迟时间参数ns位是秒,自己设计候最好使用秒,因

为实现getDelay()方法可以指定任意位,一旦以秒或分作为单位,而延时时间又精确不到

秒就麻了。使用时请注意当time小于当前时间时getDelay会返回数。

第三步:实现compareTo方法来指定元素的序。例如,时时间的放在列的末

尾。实现如下。public int compareTo(Delayed other) {

if (other == this) // compare zero ONLY if same object

return 0;

if (other instanceof ScheduledFutureTask) {

ScheduledFutureTask<> x = (ScheduledFutureTask<>)other;

long diff = time - x.time;

if (diff < 0)

return -1;

else if (diff > 0)

return 1;

else if (sequenceNumber < x.sequenceNumber)

return -1;

else

return 1;

}

long d = (getDelay(TimeUnit.NANOSECONDS) -

other.getDelay(TimeUnit.NANOSECONDS));

return (d == 0) 0 : ((d < 0) -1 : 1);

}

2)如何实现阻塞

阻塞列的实现简单,当消者从列里取元素,如果元素没有达到延时时

,就阻塞当前线程。

long delay = first.getDelay(TimeUnit.NANOSECONDS);

if (delay <= 0)

return q.poll();

else if (leader != null)

available.await();

else {

Thread thisThread = Thread.currentThread();

leader = thisThread;

try {

available.awaitNanos(delay);

} finally {

if (leader == thisThread)

leader = null;

}

}

中的leader是一个等待部元素的线程。如果leader不等于空,表示已

线程在等待列的元素。所以,使用await()方法当前线程等待信号。如果leader

等于空,把当前线置成leader,并使用awaitNanos()方法当前线程等待接收信号或等

delay时间5.SynchronousQueue

SynchronousQueue是一个不存元素的阻塞列。每一个put操作必等待一个take操作,

不能继续添加元素。

它支持公平访问队列。默情况下线程采用非公平性策略访问队列。使用以下构造方法

可以建公平性访问SynchronousQueue,如果true等待的线程会采用先先出的

访问队列。

public SynchronousQueue(boolean fair) {

transferer = fair new TransferQueue() : new TransferStack();

}

SynchronousQueue可以看成是一个球手,负责把生线理的数据直接传递给

线程。列本身并不存任何元素,非常适合传递景。SynchronousQueue的吞吐量高于

LinkedBlockingQueueArrayBlockingQueue

6.LinkedTransferQueue

LinkedTransferQueue是一个由成的无界阻塞TransferQueue列。相于其他阻

列,LinkedTransferQueue多了tryTransfertransfer方法。

1transfer方法

如果当前有消者正在等待接收元素(消者使用take()方法或带时间限制的poll()方法

),transfer方法可以把生入的元素立刻transfer传输者。如果没有消者在等

待接收元素,transfer方法会将元素存放在列的tail点,并等到元素被消者消了才返

回。transfer方法的关如下。

Node pred = tryAppend(s, haveData);

return awaitMatch(s, pred, e, (how == TIMED), nanos);第一行代试图把存放当前元素的s点作tail点。第二行代CPU自旋等待

者消元素。因自旋会消耗CPU,所以自旋一定的次数后使用Thread.yield()方法来

当前正在行的线程,并行其他线程。

2tryTransfer方法

tryTransfer方法是用来探生入的元素是否能直接传给者。如果没有消者等

待接收元素,返回false。和transfer方法的区tryTransfer方法无者是否接收,方法

立即返回,而transfer方法是必等到消者消了才返回。

时间限制的tryTransferE elong timeoutTimeUnit unit)方法,试图把生

的元素直接传给者,但是如果没有消者消费该元素等待指定的时间再返回,如果超

时还没消元素,返回false,如果在超时时间内消了元素,返回true

7.LinkedBlockingDeque

LinkedBlockingDeque是一个由成的双向阻塞列。所双向列指的是可以

列的两端插入和移出元素。双向列因多了一个操作列的入口,在多线程同

,也就减少了一半的争。相比其他的阻塞列,LinkedBlockingDeque多了addFirst

addLastofferFirstofferLastpeekFirstpeekLast等方法,以First单词结尾的方法,表示插入、

取(peek)或移除双端列的第一个元素。以Last单词结尾的方法,表示插入、取或移除双

列的最后一个元素。另外,插入方法add等同于addLast,移除方法remove等效于

removeFirst。但是take方法却等同于takeFirst,不知道是不是JDKbug,使用时还是用First

Last的方法更清楚。

在初始化LinkedBlockingDeque可以置容量防止其度膨。另外,双向阻塞列可以

运用在工作窃取模式中。

 

下面是用 阻塞队列实现的生产者消费者模式

import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueTest {

    BlockingQueue blockingQueue = null;

    public BlockingQueueTest(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    public static void main(String[] args){
//        BlockingQueueTest blockingQueueTest = new BlockingQueueTest(new SynchronousQueue<>());
        BlockingQueueTest blockingQueueTest = new BlockingQueueTest(new ArrayBlockingQueue(3));
        BlockingQueue blockingQueue =  blockingQueueTest.getBlockingQueue();

        Thread thread = new Thread(new Consumer(blockingQueue));
        thread.start();

        Thread thread2 = new Thread(new Producer(blockingQueue));
        thread2.start();

    }

    public BlockingQueue getBlockingQueue() {
        return blockingQueue;
    }
}

class Producer implements Runnable{

    BlockingQueue blockingQueue = null;

    public Producer(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {

        System.out.println("生产者启动");
        while (true) {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("请生产》》");
                String mes = scanner.nextLine();
                blockingQueue.add(mes);
                System.out.println("blockingQueue size" + blockingQueue.size());
            }
        }
    }
}

class Consumer implements Runnable{

    BlockingQueue blockingQueue = null;

    public Consumer(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {

        System.out.println("消费者启动");

        while (true) {
            try {

                System.out.println("准备拿数据");
                String mes = blockingQueue.take();
                TimeUnit.SECONDS.sleep(1);
                System.out.println("消费" + mes);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

运行效果 手速够快会出现

java ArrayBlockingQueue 阻塞队列实现的生产者消费者模式_第1张图片

你可能感兴趣的:(java)