3.3.6-1 ArrayBlockingQueue简单分析
构造方法:public ArrayBlockingQueue(int capacity) {
this(capacity, false);
} 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();
} ArrayBlockingQueue的内部元素都放在一个对象数组中 items /** The queued items */ final Object[] items;
添加元素的时候用 offer()和put()方法。用offer方法的时候如果数组已经满了,那么会返回false。但是put方法
如果数组满了的话那么他会一直等待,知道队列中有空闲的位置。
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 {
if (count == items.length) //假如当前容纳的元素个数已经等于数组长度,那么返回false
return false;
else {
enqueue(e); // 将元素插入到队列中,返回true
return true;
}
} finally {
lock.unlock(); //释放锁
}
}
put方法源码:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //可中断的获取锁
try {
while (count == items.length) //当线程从等待中被唤醒时,会比较当前队列是否已经满了
notFull.await(); //notFull = lock.newCondition 表示队列不满这种状况,假如现场在插入的时候
enqueue(e); //当前队列已经满了时,则需要等到这种情况的发生。
} finally { //可以看出这是一种阻塞式的插入方式
lock.unlock();
}
} 如果我们想要取出元素,那么我们可以采用 poll()和take()方法,如果队列中没有元素了,那么poll会返回null,但是take不会,他会一直等待,直到队列有可用的元素。 poll()方法源码: public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue(); //假如当前队列中的元素为空,返回null,否则返回出列的元素
} finally {
lock.unlock();
}
} take源码: take方法和put方法相互对应,他一定要拿到一个元素,除非线程被中断。 public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) //线程在刚进入 和 被唤醒时,会查看当前队列是否为空
notEmpty.await(); //notEmpty=lock.newCondition表示队列不为空的这种情况。假如一个线程进行take
return dequeue(); //操作时,队列为空,则会一直等到到这种情况发生。
} finally {
lock.unlock();
}
} peek源码: 如前文所说,peek方法不会真正的从队列中删除元素,实际上只是取出头元素而已。 public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
// 实际上 itemAt 方法就是 return (E) items[i];
//也就是说 返回 数组中的第i个元素。 } finally { lock.unlock(); }}
remove源码: remove方法实现在Abstract方法中,很容易看懂,里面就是走的poll方法。 public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
} enqueue源码: 我们再来看看真正得到入队操作,不然光看上面的截图也不明白不是。 private void enqueue(E x) { //因为调用enqueue的方法都已经同步过了,这里就不需要在同步了
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x; //putIndex是下一个放至元素的坐标
if (++putIndex == items.length) //putIndex+1, 并且比较是否与数组长度相同,是的话,则从数组开头
putIndex = 0; //插入元素,这就是循环数组的奥秘了
count++; //当前元素总量+1
notEmpty.signal(); //给等到在数组非空的线程一个信号,唤醒他们。
} dequeue源码: 当然,我们也要看一下出对的操作 private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null; //将要取出的元素指向null 表示这个元素已经取出去了
if (++takeIndex == items.length) //takeIndex +1,同样的假如已经取到了数组的末尾,那么就要重新开始取
takeIndex = 0; //这就是循环数组
count--;
if (itrs != null)
itrs.elementDequeued(); //这里实现就比较麻烦,下次单独出一个吧,可以看看源码
notFull.signal(); //同样 需要给 等待数组不满这种情况的线程一个信号,唤醒他们。
return x;
} 练习的例子:
package BlockQueueTEst; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; /**
* Created by zzq on 2018/2/27.
*/
public class BTest {
static ArrayBlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(3);
private static class TestT implements Runnable{ public void run() {
try {
System.out.println(blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
;
}
}
private static class TestT2 implements Runnable{ public void run() {
blockingQueue.offer("1");
}
}
// public static void main(String[] args) throws InterruptedException {
// blockingQueue.offer("1");
// blockingQueue.offer("2");
// blockingQueue.offer("3");
// blockingQueue.offer("4");
// blockingQueue.poll();
// blockingQueue.poll();
//
// blockingQueue.offer("5");
// System.out.println(blockingQueue.peek());
//
//// LinkedBlockingQueue linkedBlockingQueue=new LinkedBlockingQueue();
// }
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new TestT());
Thread t2=new Thread(new TestT2());
t1.start();
Thread.sleep(2000);
t2.start();
}
}
3.3.6-1 ArrayBlockingQueue简单分析的更多相关文章
- 简单分析ThreadPoolExecutor回收工作线程的原理
最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了. 不过,我倒是对线程池是如何回收工 ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...
- CSipSimple 简单分析
简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...
- C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法
对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...
- 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化
序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...
- 简单分析Java的HashMap.entrySet()的实现
关于Java的HashMap.entrySet(),文档是这样描述的:这个方法返回一个Set,这个Set是HashMap的视图,对Map的操作会在Set上反映出来,反过来也是.原文是 Returns ...
- Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()
ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg资料来源简单分析:libswscale的sws_getContext()
===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...
随机推荐
- Java-Runoob-面向对象:Java Override/Overload
ylbtech-Java-Runoob-面向对象:Java Override/Overload 1.返回顶部 1. Java 重写(Override)与重载(Overload) 重写(Override ...
- 杂项:Hadoop
ylbtech-杂项:Hadoop Hadoop是一个由Apache基金会所开发的分布式系统基础架构.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储.Ha ...
- UUID是如何保证全局唯一的
UUID由以下几部分的组合: (1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同. 据说时间粒度很 ...
- 关于模拟admin实现stark组件的知识点
一. url知识 还记得include分发么?里面的参数都可以有些什么? urlconf_module本质是返回的是模块路径对象 def include(arg, namespace=None, ap ...
- 由 MySQL server 和 mysql-connector 版本的不匹配引发的一场惊魂
剧情还原 今天原计划给领导演示一个小Demo, 昨天在自己机器上调通OK以后就下班了... 今天上午早会后,领导说 “昨天,我让我们IT同事把新的测试环境搭建好了,XXX 你把要演示的Demo部署到上 ...
- 第七章 HTTP流量管理(一) gateway
Gateway: istio的一种资源类型,Istio Gateway告诉k8s的istio-ingressgateway pods可以打开哪些主机和端口(如下的80是 ingressgateway ...
- 通过TortoiseGit来使用Github或Visual Studio Online版本控制管理
一.前言 关于Git源码不再多阐述,它就是一款开源分布式版本控制工具,它在源码管理领土上目前为止,使用者比例很大,越来越多的人使用该工具来管理项目源码,且相当多的开源的项目都移步到Github中,如: ...
- JS回调函数(深入篇)
<有些错别字> 在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中 ...
- delphi XE8 Android SDK SET
delphi XE Berlin Android SDK SET RAD>Tools>Options>SDk Manager> https://dl-ssl.google.co ...
- mysql utf8方式连接查看表数据乱码的问题
起因 今天在公司第一次链接一个新的mysql数据库,我看到在spring里配置的jdbc里datasource.url里有配置characterEncoding=utf8..然后就用navicat开选 ...