- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java并发编程之阻塞队列详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
1、什么是阻塞队列? 队列是一种数据结构,它有两个基本操作:在队列尾部加入一个元素,从队列头部移除一个元素。阻塞队里与普通的队列的区别在于,普通队列不会对当前线程产生阻塞,在面对类似消费者-生产者模型时,就必须额外的实现同步策略以及线程间唤醒策略。使用阻塞队列,就会对当前线程产生阻塞,当队列是空时,从队列中获取元素的操作将会被阻塞,当队列是满时,往队列里添加元素的操作也会被阻塞.
2、主要的阻塞队列及其方法 。
java.util.concurrent包下提供主要的几种阻塞队列,主要有以下几个:
1)ArrayBlockingQueue:基于数组实现的阻塞队列,在创建ArrayBlockingQueue对象时必须指定其容量大小,还可以指定访问策略,默认情况下为非公平的,即不保证等待时间最长的线程最优先能够访问队列。 2)、LinkedBlockingQueue:基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE。 3)、以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限(通过源码就可以知道,它没有容器满的信号标志),前面2种都是有界队列。 4)、DelayQueue:基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞.
阻塞队列包括了非阻塞队列中的大部分方法,还提供另外若干非常有用的方法:
put方法用来向队尾存入元素,如果队列满,则等待; take方法用来从队首取元素,如果队列为空,则等待; offer方法用来向队尾存入元素,如果队列满,则等待一定的时间,当时间期限达到时,如果还没有插入成功,则返回false;否则返回true; poll方法用来从队首取元素,如果队列空,则等待一定的时间,当时间期限达到时,如果取到,则返回null;否则返回取得的元素; 。
下面看一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import
java.util.concurrent.ArrayBlockingQueue;
/**
* @author 作者:徐剑 E-mail:anxu_2013@163.com
* @version 创建时间:2016年3月20日 下午12:52:53
* 类说明
*/
public
class
BlockingQueue
{
public
static
void
main(String[] args)
throws
InterruptedException
{
java.util.concurrent.BlockingQueue<String> blockingQueue =
new
ArrayBlockingQueue<>(
5
);
for
(
int
i =
0
; i <
10
; i++)
{
// 将指定元素添加到此队列中
blockingQueue.put(
"加入元素"
+ i);
System.out.println(
"向阻塞队列中添加了元素:"
+ i);
}
System.out.println(
"程序到此运行结束,即将退出----"
);
}
}
|
当限制阻塞队列数量为5时,添加了5个元素之后,继续添加将会队列外阻塞等待,此时程序并未终止.
当队列满了之后,我们将队首元素移除,则可以继续向阻塞队列中添加元素,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
BlockingQueue
{
public
static
void
main(String[] args)
throws
InterruptedException
{
java.util.concurrent.BlockingQueue<String> blockingQueue =
new
ArrayBlockingQueue<>(
5
);
for
(
int
i =
0
; i <
10
; i++)
{
// 将指定元素添加到此队列中
blockingQueue.put(
"加入元素"
+ i);
System.out.println(
"向阻塞队列中添加了元素:"
+ i);
if
(i>=
4
)
System.out.println(
"移除队首元素"
+blockingQueue.take());
}
System.out.println(
"程序到此运行结束,即将退出----"
);
}
|
执行结果如下:
3、阻塞队列的实现原理 下面主要看一下ArrayBlockingQueue的实现原理.
首先看一下ArrayBlockingQueue类的成员变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
class
ArrayBlockingQueue<E>
extends
AbstractQueue<E>
implements
BlockingQueue<E>, java.io.Serializable {
/** 底层存储结构-数组 */
final
Object[] items;
/** 队首元素下标 */
int
takeIndex;
/** 队尾元素下标 */
int
putIndex;
/**队列元素总数 */
int
count;
/** 重入锁 */
final
ReentrantLock lock;
/** notEmpty等待条件 */
private
final
Condition notEmpty;
/** notFull等待条件 */
private
final
Condition notFull;
/**
* Shared state for currently active iterators, or null if there
* are known not to be any. Allows queue operations to update
* iterator state.
*/
transient
Itrs itrs =
null
;
|
可以看到,ArrayBlockingQueue用来存储元素的实际上是一个数组.
再看下ArrayBlockingQueue两个重要方法的实现,put()和take()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
void
put(E e)
throws
InterruptedException
{
//先检查e是否为空
checkNotNull(e);
//获取锁
final
ReentrantLock lock =
this
.lock;
lock.lockInterruptibly();
try
{
//当队列已满,进入条件等待
while
(count == items.length)
notFull.await();
//队列不满,进行入队列操作
enqueue(e);
}
finally
{
//释放锁
lock.unlock();
}
}
|
再看下具体的入队操作:
1
2
3
4
5
6
7
8
9
10
11
12
|
private
void
enqueue(E x)
{
final
Object[] items =
this
.items;
//队尾入队
items[putIndex] = x;
if
(++putIndex == items.length)
putIndex =
0
;
//队列总数+1
count++;
//notempty条件的等待集中随机选择一个线程,解除其阻塞状态
notEmpty.signal();
}
|
下面是take()方法的源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
E take()
throws
InterruptedException
{
//获取锁
final
ReentrantLock lock =
this
.lock;
lock.lockInterruptibly();
try
{
//队列为空
while
(count ==
0
)
//线程加入notEmpty条件等待集
notEmpty.await();
//非空,出队列
return
dequeue();
}
finally
{
//释放锁
lock.unlock();
}
}
|
4、阻塞队列的应用:实现消费者-生产者模式 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/**
* @author 作者:徐剑 E-mail:anxu_2013@163.com
* @version 创建时间:2016年3月20日 下午2:21:55
* 类说明:阻塞队列实现的消费者-生产者模式
*/
public
class
Test
{
private
int
queueSize =
10
;
private
ArrayBlockingQueue<Integer> queue =
new
ArrayBlockingQueue<Integer>(queueSize);
public
static
void
main(String[] args)
{
Test test =
new
Test();
Producer producer = test.
new
Producer();
Consumer consumer = test.
new
Consumer();
producer.start();
consumer.start();
}
class
Consumer
extends
Thread
{
@Override
public
void
run()
{
consume();
}
private
void
consume()
{
while
(
true
)
{
try
{
queue.take();
System.out.println(
"从队列取走一个元素,队列剩余"
+ queue.size() +
"个元素"
);
}
catch
(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
class
Producer
extends
Thread
{
@Override
public
void
run()
{
produce();
}
private
void
produce()
{
while
(
true
)
{
try
{
queue.put(
1
);
System.out.println(
"向队列取中插入一个元素,队列剩余空间:"
+ (queueSize - queue.size()));
}
catch
(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助.
最后此篇关于Java并发编程之阻塞队列详解的文章就讲到这里了,如果你想了解更多关于Java并发编程之阻塞队列详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我遇到一种情况,我需要从某个主题读取(正在进行的)消息并将它们放入另一个 Queue 中。我怀疑我是否需要 jms Queue 或者我可以对内存中的 java Queue 感到满意。我将通过同一 jv
队列的定义 队列(Queue):先进先出的线性表 队列是仅在队尾进行插入和队头进行删除操作的线性表 队头(front):线性表的表头端,即可删除端 队尾(rear):线性表的表尾端,即可插入端 由于这
Redis专题-队列 首先,想一想 Redis 适合做消息队列吗? 1、消息队列的消息存取需求是什么?redis中的解决方案是什么? 无非就是下面这几点: 0、数据可以顺序读
0. 学习目标 栈和队列是在程序设计中常见的数据类型,从数据结构的角度来讲,栈和队列也是线性表,是操作受限的线性表,它们的基本操作是线性表操作的子集,但从数据类型的角度来讲,它们与线性表又有着巨大的不
我想在 redis + Flask 和 Python 中实现一个队列。我已经用 RQ 实现了这样的查询,如果你有 Flask 应用程序和任务在同一台服务器上工作,它就可以正常工作。我想知道是否有可能创
我正在使用 Laravel 5.1,我有一个大约需要 2 分钟来处理的任务,这个任务特别是生成报告...... 现在,很明显,我不能让用户在我接受用户输入的同一页面上等待 2 分钟,而是我应该在后台处
我正在使用 Azure 队列,并且有多个不同的进程从队列中读取数据。 我的系统的构建方式假设每条消息只读取一次。 这个Microsoft article声称 Azure 队列具有至少一次传送保证,这可
我正在创建一个Thread::Queue元素数组。 我这样做是这样的: for (my $i=0; $i new; } 但是,当我在每个队列中填充这样的元素时 $queues[$index]->enq
我试图了解如何将我的 Mercurial 补丁推送到远程存储库(例如 bitbucket.org),而不必先应用它们(实际上提交它们)。我的动机是在最终完成之前首先对我的工作进行远程备份,并且能够与其
我的本地计算机上有一个 Mercurial 队列补丁,我需要与同事共享该补丁,但我不想将其提交到上游存储库。有没有一种简单的方法可以打包该补丁并与他分享? 最佳答案 mq 将补丁作为不带扩展名的文
Java 中是否有任何类提供与 Queue 相同的功能,但有返回对象的选项,并且不要删除它,只需将其设置在集合末尾? 最佳答案 Queue不直接提供这样的方法。但是,您可以使用 poll 和 add
我在Windows上使用Tortoise svn客户端,我需要能够一次提交来自不同子文件夹的更改文件-一次提交。像在提交之前将文件添加到队列中之类的?我该怎么做? Windows上是否还有另一个svn
好吧,我正在尝试对我的 DSAQueue 类进行单元测试,它显示我的 isEmpty()、isFull() 和 dequeue() 方法失败。 以下是我的 DSAQueue 代码。我认为我的 Dequ
我想尽量减少对传入请求的数据库查询。它目前需要写入 6 个不同的表。在返回响应之前不需要完成处理。因此,我考虑了 laravel 队列,但我想知道我是否也可以摆脱写入队列/作业表所需的单独查询。我可以
我正在学习队列数据结构。我想用链表创建队列。我想编程输出:10 20程序输出:队列为空-1 队列为空-1 我哪里出错了? 代码如下: class Node { int x; Node next
“当工作人员有空时,他们会根据主题的优先级列表从等待请求池中进行选择。在时间 t 到达的所有请求都可以在时间 t 进行分配。如果两名工作人员同时有空,则安排优先权分配给最近的工作最早安排的人。如果仍然
我正在开发一个巨大的应用程序,它使用一些子菜单、模式窗口、提示等。 现在,我想知道在此类应用程序中处理 Esc 和单击外部事件的正确方法。 $(document).keyup(function(e)
所以 如果我有一个队列 a --> b --> NULL; 当我使用函数时 void duplicate(QueueNodePtr pHead, QueueNodePtr *pTail) 它会给 a
我正在尝试为键盘输入实现 FIFO 队列,但似乎无法让它工作。我可以让键盘输入显示在液晶显示屏上,但这就是我能做的。我认为代码应该读取键盘输入并将其插入队列,然后弹出键盘输入并将值读取到液晶屏幕上。有
我正在学习算法和 DS。如何在 JavaScript 中使用队列? 我知道你可以做这样的事情。 var stack = []; stack.push(2); // stack is now
我是一名优秀的程序员,十分优秀!