linkedblockingqueue有界队列吗,linkedqueue队列实现
在上一篇文章中,我们介绍了由CAS算法实现的非阻塞队列ConcurrentLinedQueue,接下来,我们介绍了由排他锁实现的阻塞队列LinkedBlockingQueue。
LinkedBlockingQueue也是通过使用单向链表实现的。它还有两个节点,分别用来存储头节点和尾节点,还有一个原子变量count,初始值为0,用来记录队列元素的个数。ReentrantLock还有另外两个实例,分别用于控制入队和出队元素的原子性。takeLock用于控制同一时刻只有一个线程可以从队列头获取元素,其他线程必须等待。putLock控制同一时间只有一个线程可以获得锁,在队列末尾添加元素,其他线程必须等待。此外,notEmpty和notFull是条件变量,它们都有一个条件队列来存储入队和出队时阻塞的线程。实际上,这是一种生产者-消费者模式。以下是独占锁的创建代码。
private final atomic integer count=new atomic integer();/**由take、poll等持有的锁*/private final reentrant Lock take Lock=new reentrant Lock();/**等待队列等待takes */private final Condition notEmpty=take lock . new Condition();/**由put、offer等持有的锁*/private final reentrant Lock putLock=new reentrant Lock();/**等待puts的等待队列*/private最终条件not full=putlock . new Condition();调用线程在LinkedBlockingQueue实例上执行take、poll等操作时,需要获取takeLock,这样同一时间只有一个线程可以操作链表头节点。此外,由于条件变量notEmpty内部条件队列的维护使用了takeLock的锁状态管理机制,调用线程在调用notEmpty的await和signal方法之前必须先获取takeLock,否则会抛出IllegalMonitorStateException异常。NotEmpty在内部维护一个条件队列。当一个线程在获取takeLock后调用notEmpty的await方法时,调用线程会被阻塞,然后这个线程会被放在notEmpty内部的条件队列中等待,直到一个线程调用notEmpty的signal方法。
在对LinkedBlockingQueue实例进行put、offer等操作时,需要获取putLock,这样才能保证同一时刻只有一个线程可以操作链表的尾节点。同样,由于条件变量notFull内部条件队列的维护使用了putLock的锁状态管理机制,调用线程在调用notFull的await和signal方法之前,必须先获得putLock锁,否则会抛出IllegalMonitorStateException异常。NotFull在内部维护一个条件队列。当一个线程在获取putLock锁后调用notFull的await方法时,调用线程将被阻塞,然后该线程将被放入notFull的条件队列中等待,直到一个线程调用notFull的signal方法。以下是LinkedBlockingQueue的无参数构造函数的代码。
以下是LinkedBlockingQueue的无参数构造代码
public static final int MAX _ VALUE=0x 7 fffffff;public LinkedBlockingQueue(){ this(Integer。MAX _ VALUE);} public LinkedBlockingQueue(int capacity){ if(capacity=0)throw new IllegalAgrumentException();容量=容量;last=head=new NodeE(空);}根据这段代码,默认的队列容量是0x7fffffff,用户也可以自己指定容量,所以某种程度上可以说LinkedBlockingQueue是一个有界阻塞队列。
offer操作
public boolean offer(E E){//(1)if(E==null)throw new NullPointerException();//(2)最终AtomicInteger计数=this.countif (count.get()==capacity)返回false//(3)int c=-1;NodeE node=新NodeE(e);final reentrant lock putLock=this . putLock;putlock . lock();try {//(4)if(count . get()capacity){ enqueue(node);c=count . getandincrement();//(5) if (c 1容量)not full . signal();} }最后{//(6)putlock . unlock();}//(7)if(c==0)signal notempty();//(8)返回c=0;}代码(2)确定如果当前队列已满,则丢弃当前元素,并返回false。
代码(3)获取putLock锁。在当前线程获得锁之后,调用put和offer操作的其他线程将被阻塞(被阻塞的线程将被放置在putLock锁的AQS阻塞队列中)。
这里的代码(4)判断当前队列是否又满了,因为在代码(2)执行和获取putLock期间,其他线程可能会通过put或offer操作向队列添加新元素。如果新队列确实不满意,新元素将加入队列,并且计数器将递增。
代码(5)判断新元素入队后队列中是否还有空闲空间,唤醒notFull的条件队列中一个因调用notFull的await操作而被阻塞的线程(例如执行put方法时队列已满)。因为队列现在是空闲的,所以可以在这里提前唤醒一个排队的线程。
代码(6)释放获得的putLock锁。这里需要注意的是,锁的释放必须在finally中完成,因为即使try块抛出异常,finally也会被执行。此外,在释放锁之后,被调用put操作阻塞的其他线程之一将获得锁。
代码(7)中的C0表明,当执行代码(6)来释放锁时,队列中至少有一个元素。如果队列中有元素,将执行signalNotEmpty操作。
关于深入理解Java并发编程的LinkedBlockingQueue的这篇文章到此为止。有关Java的LinkedBlockingQueue的更多信息,请搜索以前关于流行IT的文章或继续浏览下面的相关文章。我希望你将来能支持流行它!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。