1.队列

  • 队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部)就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.

  • 在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来.说白了,就是先进先出,线程安全!

  • java中并发队列都是在java.util.concurrent并发包下的,Queue接口与List、Set同一级别,都是继承了Collection接口

2.实现一个简单的并发队列

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; /**
* @author QiuQiu&LL
* @version 1.0
*/
public class MyQueue {
//元素集合
private LinkedList<Object> list = new LinkedList<Object>();
//计数器(同步),判断集合元素数量
private AtomicInteger count = new AtomicInteger();
//集合上限与下限,final必须指定初值
private final int minSize = 0;
private final int maxSize; //构造器指定最大值
public MyQueue(int maxSize) {
this.maxSize = maxSize;
} //初始化对象,用于加锁,也可直接用this
private Object lock = new Object(); /**
* put方法:往集合中添加元素,如果集合元素已满,则此线程阻塞,直到有空间再继续
*
* @param obj
*/
public void put(Object obj) {
synchronized (lock) {
while (count.get() == this.maxSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(obj);
//计数器加一
count.incrementAndGet();
System.out.println("放入元素:" + obj);
//唤醒另一个线程,(处理极端情况:集合一开始就是空,此时take线程会一直等待)
lock.notify();
}
} /**
* take方法:从元素中取数据,如果集合为空,则线程阻塞,直到集合不为空再继续
*
* @return
*/
public Object take() {
Object result = null;
synchronized (lock) {
while (count.get() == this.minSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//移除第一个
result = list.removeFirst();
//计数器减一
count.decrementAndGet();
System.out.println("拿走元素:" + result);
//唤醒另一个线程,(处理极端情况:集合一开始就是满的,此时put线程会一直等待)
lock.notify();
}
return result;
} public int getSize() {
return this.count.get();
} public static void main(String[] args) {
//创建集合容器
MyQueue queue = new MyQueue(5);
queue.put("1");
queue.put("2");
queue.put("3");
queue.put("4");
queue.put("5");
System.out.println("当前容器长度为:" + queue.getSize());
Thread t1 = new Thread(() -> {
queue.put("6");
queue.put("7");
// 队列满了,他将会一直等待
queue.put("8");
}, "t1");
Thread t2 = new Thread(() -> {
Object take1 = queue.take();
Object take2 = queue.take();
System.out.println("take1 = " + take1 + "==== take2 = " + take2);
}, "t2");
//测试极端情况,两秒钟后再执行另一个线程,会阻塞等待另一个线程take
t1.start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}

3.JDK中并发队列提供了两种实现,一种是高性能队列ConcurrentLinkedQueue,一种是阻塞队列BlockingQueue,两种都继承自Queue

(1)ConcurrentLinkedQueue

import java.util.concurrent.ConcurrentLinkedQueue;

/**
* @author QiuQiu&LL
* @version 1.0
*/
public class ConcurrentLinkedQueueDemo {
public static void main(String[] args) {
ConcurrentLinkedQueue clq = new ConcurrentLinkedQueue();
clq.add(1);
clq.add(2);
clq.add(3);
clq.add(4); // 获取队头元素,并删除
Object poll = clq.poll();
System.out.println("poll = " + poll);
// 获取队头元素,不删除
Object peek = clq.peek();
System.out.println("peek = " + peek); System.out.println("clq = " + clq);
}
}

(2) BlockingQueue

  • blockingQueue主要有5中实现,具体如下

ArrayBlockingQueue

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit; /**
* @author QiuQiu&LL
* @version 1.0
*/
public class ArrayBlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(2);
// 添加一个元素
abq.add(1);//add :添加元素,如果BlockingQueue可以容纳,则返回true,否则抛异常,支持添加集合
boolean offerIsSuccess = abq.offer(2);//offer: 如果可能的话,添加元素,即如果BlockingQueue可以容纳,则返回true,否则返回false,支持设置超时时间
System.out.println("offerIsSuccess = " + offerIsSuccess);
//设置超时,如果超过时间就不添加,返回false,
// abq.offer(3, 2, TimeUnit.SECONDS);
//put 添加元素,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续
// abq.put(4);
new Thread(() -> {
//poll 取走头部元素,若不能立即取出,则可以等time参数规定的时间,取不到时返回null,支持设置超时时间
Integer poll = abq.poll();
System.out.println("poll = " + poll);
});
abq.poll(2,TimeUnit.SECONDS);//两秒取不到返回null
abq.offer(3, 2, TimeUnit.SECONDS);
//take() 取走头部元素,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
abq.take();
//取出头部元素,但不删除
abq.element();
List drain=new ArrayList();
//一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
int to = abq.drainTo(drain);
System.out.println("to = " + to);
System.out.println("drain = " + drain);
List list=new ArrayList();
abq.drainTo(list,2);//将队列中两个元素取到list中,取走后队列中就没有取走的元素
System.out.println(list); //[a,b]
System.out.println(abq); //[]
}
}

ArrayBlockingQueue

import java.util.concurrent.LinkedBlockingQueue;

/**
* @author QiuQiu&LL
* @version 1.0
*/
public class LinkedBlockingQueueDemo {
public static void main(String[] args) {
//可指定容量,也可不指定
LinkedBlockingQueue<String> lbq = new LinkedBlockingQueue<>();
lbq.add("a");
lbq.add("b");
lbq.add("c");
//API与ArrayBlockingQueue相同
//是否包含
System.out.println(lbq.contains("a"));
//移除头部元素或者指定元素 remove("a")
System.out.println(lbq.remove());
//转数组
Object[] array = lbq.toArray();
//element 取出头部元素,但不删除
System.out.println(lbq.element());
System.out.println(lbq.element()); System.out.println("array = " + array);
}
}

SynchronousQueue

import java.util.concurrent.SynchronousQueue;

/**
* @author QiuQiu&LL
* @version 1.0
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> sq=new SynchronousQueue<String>();
// iterator() 永远返回空,因为里面没东西。
// peek() 永远返回null
/**
* isEmpty()永远是true。
* remainingCapacity() 永远是0。
* remove()和removeAll() 永远是false。
*/
new Thread(()->{
try {
//取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。
System.out.println(sq.take());
} catch (InterruptedException e) {
e.printStackTrace();
} }).start();
new Thread(()->{
try {
//offer() 往queue里放一个element后立即返回,
//如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false
//true ,上面take线程一直在等,
////下面刚offer进去就被拿走了,返回true,如果offer线程先执行,则返回false
System.out.println(sq.offer("b")); } catch (Exception e) {
e.printStackTrace();
} }).start();
new Thread(()->{
try {
//往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走
sq.put("a");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}

DelayQueue

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit; //网民
public class Netizen implements Delayed {
//身份证
private String ID;
//名字
private String name;
//上网截止时间
private long playTime; //比较优先级,时间最短的优先
@Override
public int compareTo(Delayed o) {
Netizen netizen = (Netizen) o;
return this.getDelay(TimeUnit.SECONDS) - o.getDelay(TimeUnit.SECONDS) > 0 ? 1 : 0;
} public Netizen(String iD, String name, long playTime) {
ID = iD;
this.name = name;
this.playTime = playTime;
} //获取上网时长,即延时时长
@Override
public long getDelay(TimeUnit unit) {
//上网截止时间减去现在当前时间=时长
return this.playTime - System.currentTimeMillis();
} public String getID() {
return ID;
} public void setID(String ID) {
this.ID = ID;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public long getPlayTime() {
return playTime;
} public void setPlayTime(long playTime) {
this.playTime = playTime;
}
}
import java.util.concurrent.DelayQueue;

//网吧
public class InternetBar implements Runnable {
//网民队列,使用延时队列
private DelayQueue<Netizen> dq = new DelayQueue<Netizen>(); //上网
public void startPlay(String id, String name, Integer money) {
//截止时间= 钱数*时间+当前时间(1块钱1秒)
Netizen netizen = new Netizen(id, name, 1000 * money + System.currentTimeMillis());
System.out.println(name + "开始上网计费......");
dq.add(netizen);
} //时间到下机
public void endTime(Netizen netizen) {
System.out.println(netizen.getName() + "余额用完,下机");
} @Override
public void run() {
//线程,监控每个网民上网时长
while (true) {
try {
//除非时间到.否则会一直等待,直到取出这个元素为止
Netizen netizen = dq.take();
endTime(netizen);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
//新建一个网吧
InternetBar internetBar = new InternetBar();
//来了三个网民上网
internetBar.startPlay("001", "qiuqiu", 3);
internetBar.startPlay("002", "ll", 7);
internetBar.startPlay("003", "QIUQIU&LL", 5);
Thread t1 = new Thread(internetBar);
t1.start();
}
}

4.案例1

import java.util.ArrayDeque;
import java.util.Queue; /**
* @author QiuQiu&LL
* @version 1.0
* 模拟银行办理业务员
*/
public class MyQueueDemo {
public static void main(String[] args) {
Queue<Request> que = new ArrayDeque<Request>();
//模拟排队情况
for (int i = 0; i < 10; i++) {
final int num = i;
que.offer(new Request() {
//应用匿名内部类对象只能访问 final 修饰的变量
@Override
public void deposit() {
System.out.println("第" + num + "个人,办理存款业务,存款额度为:" + (Math.random() * 10000));
}
});
}
dealWith(que);
} //处理业务
public static void dealWith(Queue<Request> que) {
Request req = null;
while (null != (req = que.poll())) {
req.deposit();
}
}
} interface Request {
//存款
void deposit();
}

5.案例2

import java.util.ArrayDeque;
import java.util.Deque; /**
* 使用队列实现自定义堆栈
* 1、弹
* 2、压
* 3、获取头
*/
public class MyStack<E> {
//容器
private Deque<E> container = new ArrayDeque<E>();
//容量
private int cap; public MyStack(int cap) {
super();
this.cap = cap;
} //压栈
public boolean push(E e) {
if (container.size() + 1 > cap) {
return false;
}
return container.offerLast(e);
} //弹栈
public E pop() {
return container.pollLast();
} //获取
public E peek() {
return container.peekLast();
} public int size() {
return this.container.size();
}
} //测试自定义堆栈
public class MyStockDemo {
/**
* @param args
*/
public static void main(String[] args) {
MyStack<String> backHistory = new MyStack<String>(3);
backHistory.push("www.baidu.com");
backHistory.push("www.google.com");
backHistory.push("www.sina.com");
backHistory.push("www.bjsxt.cn");
System.out.println("大小:" + backHistory.size()); //遍历
String item = null;
while (null != (item = backHistory.pop())) {
System.out.println(item);
}
}
}

Java中的并发队列的更多相关文章

  1. 聊聊并发(七)——Java中的阻塞队列

    3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...

  2. Java中的阻塞队列(BlockingQueue)

    1. 什么是阻塞队列 阻塞队列(BlockingQueue)是 Java 5 并发新特性中的内容,阻塞队列的接口是 java.util.concurrent.BlockingQueue,它提供了两个附 ...

  3. 多线程编程学习六(Java 中的阻塞队列).

    介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取 ...

  4. JUC之Java中的阻塞队列及其实现原理

    在文章线程池实现原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线程池的组成部分,其中一个组成部分就是阻塞队列.那么JAVA中的阻塞队列如何实现的呢? 阻塞队列,关键字是阻塞 ...

  5. Java中的阻塞队列-ArrayBlockingQueue(一)

    最近在看一些java基础的东西,看到了队列这章,打算对复习的一些知识点做一个笔记,也算是对自己思路的一个整理,本章先聊聊java中的阻塞队列 参考文章: http://ifeve.com/java-b ...

  6. Java 中的并发工具类

    Java 中的并发工具类 CountDownLatch public class JoinCountDownLatchTest { public static void main(String[] a ...

  7. 阻塞队列一——java中的阻塞队列

    目录 阻塞队列简介:介绍阻塞队列的特性与应用场景 java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列 BlockQueue中方法:介绍阻塞队列的API接口 阻塞队列的实现原理:具体的例子 ...

  8. JAVA中关于并发的一些理解

    一,JAVA线程是如何实现的? 同步,涉及到多线程操作,那在JAVA中线程是如何实现的呢? 操作系统中讲到,线程的实现(线程模型)主要有三种方式: ①使用内核线程实现 ②使用用户线程实现 ③使用用户线 ...

  9. JAVA多线程(二) 并发队列和阻塞队列

    github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service/ ...

  10. Java中的并发库学习总结

    我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便.而当针对高质量Java ...

随机推荐

  1. 《SQL与数据库基础》23. 读写分离

    目录 读写分离 一主一从 准备 配置 双主双从 准备 配置 主库配置 从库配置 从库关联主库 主库相互复制 双主双从读写分离 本文以 MySQL 为例.以 MyCat 数据库中间件为例,通过 MyCa ...

  2. Python 创建或读取 Excel 文件

    Excel是一种常用的电子表格软件,广泛应用于金融.商业和教育等领域.它提供了强大的数据处理和分析功能,可进行各种计算和公式运算,并能创建各种类型的图表和可视化数据.Excel的灵活性使其成为处理和管 ...

  3. 为什么要使用API接口,他能带来哪些便利

    API接口是程序员进行应用程序开发时不可或缺的工具之一.以下是使用API接口的一些优点: 数据交换:使用API接口可以使不同的应用程序.网站或服务之间交换数据更为便捷,减少人工输入数据的时间和风险. ...

  4. Spring Bean 的作用域(Bean Scope)

    前言 大家好,我是 god23bin,今天我们来聊一聊 Spring 框架中的 Bean 作用域(Scope). 什么是 Bean 的作用域? 我们在以 XML 作为配置元数据的情况下,进行 Bean ...

  5. Dockcer上传hub和配置国内镜像源

    Dockcer上传hub和配置国内镜像源 1.Dockcer上传hub 要将本地的Docker镜像上传到Docker镜像仓库,可以按照以下步骤操作: linux环境 1.创建用户 首先,确保你已经在D ...

  6. 【Redis】SpringBoot集成Redis事务-亲测

    大家好,我是mep.今天一起来探讨一下Redis缓存的问题,SpringBoot如何集成Redis网上文章很多,基本都是介绍如何配置redisTemplate,如何调用,本文就不过多介绍了.这次我们研 ...

  7. JUC并发编程(1)—CompletableFuture详解

    @ 目录 CompletableFuture介绍 1.创建异步任务 2.CompletableFuture API ①. 获得结果和触发计算(get.getNow.join.complete) ②. ...

  8. Skywalking APM监控系列(一丶.NET5.0+接入Skywalking监听)

    前言 新项目采用的abp vnext的微服务模块化架构,所以把应用的服务拆成了很多独立模块 在初期,我们通过日志还能跟踪到问题, 后期服务越来越多(大约扩充到了十几个),随着调用链路越来越深 ,问题也 ...

  9. Required request body is missing缺失请求体

    今天在写项目的时候前台传的参数后台一直接收不到,在网上搜了一些东西试了也没效果.后来发现是因为加了@RequestBody 去掉之后再次尝试就可以了.

  10. json数组格式问题

    ---恢复内容开始--- 使用jsonserver来模拟后台数据接口时犯了一个很低级的错误 找了很久没有发现有什么不对劲的地方,后来仔细发现原来是一个很细微的语法问题:}]  中间不能有逗号!! -- ...