Java并发学习笔记(八)-LinkedBlockingQueue

本文详细探讨了Java并发编程中的LinkedBlockingQueue。这个由链表实现的阻塞队列,默认容量为Integer.MAX_VALUE,可指定容量以避免过度扩张。队列内部使用两个指针管理头尾,并采用两把锁(putLock和takeLock)提高并发性能。put方法在队列满时阻塞,而take方法在队列空时阻塞,两者在操作完成后能唤醒相应等待的线程。队列的插入发生在队尾,取出则从队头进行。
摘要由CSDN通过智能技术生成

LinkedBlockingQueue是由链表组成的阻塞队列,先来看demo

 

public class LinkedBlockingQueueDemo {

	public static void main(String[] args) {
		ExecutorService es = Executors.newCachedThreadPool();
		BlockingQueue<Bread> queue = new LinkedBlockingQueue<Bread>(10);
		for(int i = 0; i < 2; i++){
			es.execute(new Baker(queue));
		}
		
		for(int i = 0; i < 10; i++){
			es.execute(new BreadConsumer(queue));
		}
		es.shutdown();
	}

}

class Baker implements Runnable{
	private static int no = 0;
	private int id = ++no;
	private int count = 0;
	
	private BlockingQueue<Bread> queue;
	
	public Baker(BlockingQueue<Bread> queue){
		this.queue = queue;
	}

	@Override
	public void run() {
		for(int i = 0; i < 10; i++){
			System.out.println("面包师" + id + "正准备做第" + ++count + "面包");
			Bread bread = new Bread();
//			满队列情况下,阻塞
			try {
				queue.put(bread);
				System.out.println("面包师" + id + "做的第" + count + "面包是面包"+ bread.getId());
			} catch (InterruptedException e) {
					
			}
		}	
	}
}

class BreadConsumer implements Runnable{
	private static int no = 0;
	private int id = ++no;
	private int count = 0;
	
	private BlockingQueue<Bread> queue;
	
	public BreadConsumer(BlockingQueue<Bread> queue){
		this.queue = queue;
	}
	
	@Override
	public void run() {
		for(int i = 0; i < 2; i++){
			System.out.println("顾客 " + id + "准备买第" + ++count +"个面包" );
			Bread bread = null;
//		空队列情况下,阻塞
			try {
				bread = queue.take();
			} catch (InterruptedException e) {
				
			}
		<span style="white-space:pre">	</span>System.out.println("顾客" + id + "买到的第" +count+"个面包是面包" + bread.getId());
		}
	}
	
}

class Bread {
	private static int count = 0;
	private int id = ++count;
	public int getId() {
		return id;
	}
}

 

默认情况下,其容量为Integer.MAX_VALUE

 

    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

也可以指定一个容量以免链表过度扩张

 

 

    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

在LinkedBlockingQueue的链表里,有两个指针,分别指向队列头和队列尾。与ArrayBlockingQueue不同的是,在LinkedBlockingQueue里,有两把锁,分别锁队列头和队列尾。这样做是有好处的,可以同时入队和出队,比ArrayBlockingQueue性能高

    /** 链表头 */
    private transient Node<E> head;

    /** 链表尾 */
    private transient Node<E> last;

    /** 出队的锁 */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** 入队的锁 */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

我们来看put阻塞方法,如果队列满,会一直阻塞,直到队列不满

 

 

    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        int c = -1;
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) { 
                    notFull.await();
            }
            enqueue(e);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

往队列里插入数据时,首先会持有putLock锁,如果当前队列元素个数跟容量相等,阻塞,调用notFull.await。不然,入队。入队后如果元素个数小于队列容量,会唤醒其它的阻塞的插入线程。

队列里,插入元素,会插入队尾

 

    private void enqueue(E x) {
        // assert putLock.isHeldByCurrentThread();
        last = last.next = new Node<E>(x);
    }

再来看出队操作take,也是阻塞方法

 

 

    public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
                while (count.get() == 0) {
                    notEmpty.await();
                }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

出队获取的是takeLock,类似的,如果当前队列元素个数为0,阻塞,调用notEmpty.await。不然,出队。出队后如果元素个数大于0,会唤醒其它的阻塞的出队线程。出队从队头出队

 

 

    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值