当前位置: 首页 > 后端技术 > Java

【JAVA并发编程】阻塞队列LinkedBlockingQueue源码解读

时间:2023-04-01 14:54:46 Java

1.简介什么是阻塞队列?我们都知道队列具有先进先出的特性,所以具有阻塞特性的队列(即队列中全是阻塞的生产者,队列中没有阻塞的消费者)称为阻塞队列。阻塞队列广泛用于生产者-消费者模式。在实际开发中,我们经常使用LinkedBlockingQueue作为阻塞队列。为什么使用LinkedBlockingQueue而不是ArrayBlockingQueue?下面将通过解读LinkedBlockingQueue的源码来回答这个问题。2.LinkedBlockingQueue的基本使用2.1基本结构LinkedBlockingQueue是一个基于单向链表的队列。结构图如下2.2常用的apiBlockingQueue接口提供了很多方法,常用的有:method\processingmethod抛出异常并返回特殊值并保持阻塞超时exitinsertadd(e)offer(e)put(e)put(e,time,unit)removeremove(e)poll()take()poll(time,unit)checkelement()peek()——可以看到,有4种插入和移除方法元素,每个方法都有对应的处理方法(抛出异常,返回特殊值,一直阻塞,超时退出)2.3简单例子下面是一个模拟生产者-消费者的简单例子,生产者线程定时往阻塞队列中插入消息,消费者线程不断地从阻塞队列中消费消息publicstaticvoidmain(String[]args){newThread(newProducer()).start();新线程(新消费者())。开始();}staticclassProducerimplementsRunnable{@Overridepublicvoidrun(){intcount=0;while(true){try{queue.put("message"+count++);TimeUnit.MILLISECONDS.sleep(500);}赶上(InterruptedExceptione){e.printStackTrace();}}}}staticclassConsumerimplementsRunnable{@Overridepublicvoidrun(){while(true){try{Stringmsg=queue.take();System.out.println("消费者要留言:"+msg);}catch(InterruptedExceptione){e.printStackTrace();}}}}}3.源码解读3.1关键属性intcapacity:队列容量AtomicIntegercount:当前队列包含的元素个数Nodehead:头节点Nodelast:尾节点ReentrantLocktakeLock:takelockConditionnotEmpty:队列不为空(condition)ReentrantLockputLock:放锁ConditionnotFull:队列未满(condition)可以看到这里有两把锁,一个是取元素的take锁,而另一个是用于插入元素的放置锁。这是与ArrayBlockingQueue的重要区别。队列中有足够的空间来存储元素。大致步骤如下:putLock锁判断队列元素数量是否达到容量,如果达到则调用notFull条件的await方法,使当前生产者线程进入等待状态,直到被唤醒。否则,下一步是将当前元素插入到队列的尾部。如果队列未满,它会唤醒一个等待notFull条件的生产者线程来putLock解锁。如果原始队列为空,则唤醒一个正在等待notEmpty条件的消费者线程。源码如下:publicvoidput(Ee)throwsInterruptedException{if(e==null)thrownewNullPointerException();intc=-1;节点节点=新节点(e);finalReentrantLockputLock=this.putLock;最终AtomicInteger计数=this.count;//PutLock被锁定putLock.lockInterruptibly();try{//判断队列元素个数是否达到容量,如果达到则调用notFull条件的await方法使当前生产者线程进入等待状态while(count.get()==capacity){notFull.await();}//当还有空闲位置或者当前线程被唤醒时,将当前元素插入队尾enqueue(node);c=count.getAndIncrement();//如果队列未满,它将唤醒一个生产者线程等待notFull条件if(c+11)notEmpty.signal();}finally{//takeLock解锁takeLock.unlock();}//如果原来的队列满了,唤醒一个等待notFull条件的production或者threadif(c==capacity)signalNotFull();returnx;}4.小结阻塞队列在并发编程中被广泛使用,常用于生产者-消费者模式。当队列满或空时,它可以阻塞生产者或消费者,直到队列满或非空。在实际开发过程中,一般使用LinkedBlockingQueue代替ArrayBlockingQueue。因为它内部有putLock和takeLock两把锁,也就是说消费者可以消费数据,而生产者在生产数据,而ArrayBlockingQueue内部只有一把锁。在生产数据的同时,消费者不能消费数据。