SynchronousQueueDemo
1.ArrayDeque, (数组双端队列)
2.PriorityQueue, (优先级队列)
3.ConcurrentLinkedQueue, (基于链表的并发队列)
4.DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口)
5.ArrayBlockingQueue, (基于数组的并发阻塞队列)
6.LinkedBlockingQueue, (基于链表的FIFO阻塞队列)
7.LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列)
8.PriorityBlockingQueue, (带优先级的无界阻塞队列)
9.SynchronousQueue (并发同步阻塞队列)
SynchronousQueue 没有篮子,是一个现做现卖的原理。
Java 6的并发编程包中的SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。
数据是在配对的生产者和消费者线程之间直接传递的,并不会将数据缓冲数据到队列中。可以这样来理解:生产者和消费者互相等待对方,握手,然后一起离开。
SynchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。
阻塞算法实现通常在内部采用一个锁来保证多个线程中的put()和take()方法是串行执行的。采用锁的开销是比较大的,还会存在一种情况是线程A持有线程B需要的锁,B必须一直等待A释放锁,即使A可能一段时间内因为B的优先级比较高,B一直占有时间片,A一直不能获得时间片,而得不到时间片运行。所以在高性能的应用中我们常常希望规避锁的使用。
放的线程要别的线程取走了才返回,否侧等待。取得时候为空也等待。一个放一个取一个放一个取这样执行下去。
package com.itmayiedu; import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit; public class SynchronousQueueDemo {
public static void main(String[] args) throws InterruptedException {
final SynchronousStack<String> queue = new SynchronousStack<String>(true);
// final SynchronousQueue1<String> queue = new SynchronousQueue1<String>(true); Thread put1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put thread start");
try {
queue.put("put1" );
} catch ( Exception e) {
}
System.out.println("put thread end");
}
},"put1");
Thread put2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put thread start");
try {
queue.put("put2" );
} catch (Exception e) {
}
System.out.println("put thread end");
}
},"put2");
Thread put3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put thread start");
try {
queue.offer("put3" ,,TimeUnit.SECONDS);
} catch (Exception e) {
}
System.out.println("put thread end");
}
},"put3");
Thread put4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put thread start");
try {
queue.put("put4" );
} catch (Exception e) {
}
System.out.println("put thread end");
}
},"put4"); Thread take1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("take thread start");
try {
System.out.println("take from putThread: " + queue.take());
} catch (Exception e) {
}
System.out.println("take thread end");
}
},"take1");
Thread take2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("take thread start");
try {
System.out.println("take from putThread: " + queue.take());
} catch (Exception e) {
}
System.out.println("take thread end");
}
},"take2");
Thread take3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("take thread start");
try {
System.out.println("take from putThread: " + queue.take());
} catch (Exception e) {
}
System.out.println("take thread end");
}
},"take3"); put1.start();
put2.start();
put3.start();
put4.start();
// take1.start();
// take2.start();
// take3.start();
}
}
public class NativeSynchronousQueue<E> {
boolean putting = false;
E item = null; public synchronized E take() throws InterruptedException {
while (item == null)//没有元素就等
wait();
E e = item;
item = null;
notifyAll();//目的是唤醒放的线程,有可能唤醒了取的线程(继续等)
return e;
} public synchronized void put(E e) throws InterruptedException {
if (e == null)
return;
while (putting)//有人在放,就等待
wait();//此处唤醒,继续等待
putting = true;
item = e;
notifyAll();//目的是唤醒取的线程,有可能唤醒了放的线程(继续等),有可能唤醒了自己(继续等)
while (item != null)//没人取走,就等待
wait();
putting = false;
notifyAll();
}
}
public class SemaphoreSynchronousQueue<E> {
E item = null;
Semaphore sync = new Semaphore();
Semaphore send = new Semaphore();
Semaphore recv = new Semaphore();//一定先release再acquire public E take() throws InterruptedException {
recv.acquire();//放线程put好了,取的线程就可以取
E x = item;
sync.release();//取完了,当前放的线程可以走了,
send.release();//取完了,下个线程可以放了,
return x;
} public void put (E x) throws InterruptedException{
send.acquire();//只能一个线程放,防止item不安全,
item = x;
recv.release();//我已经put好了,取的线程可以取了,
sync.acquire();//取完了,当前放的线程可以走了,
}
}
放的线程1放,放好了,通知取的线程1取,取完了通知放的线程2放,放好了,通知取线程2去取,依次。。
在多核机器上,上面方法的同步代价仍然较高,操作系统调度器需要上千个时间片来阻塞或唤醒线程,而上面的实现即使在生产者put()时已经有一个消费者在等待的情况下,阻塞和唤醒的调用仍然需要。
上面的实现是有一个位置可以去放,但是放的线程要等到取走了才能返回。
SynchronousQueue是生产线程将东西直接交给消费线程。不需要容器。
Java 6的SynchronousQueue的实现采用了一种性能更好的无锁算法 — 扩展的“Dual stack and Dual queue”算法。性能比Java5的实现有较大提升。竞争机制支持公平和非公平两种:非公平竞争模式使用的数据结构是后进先出栈(Lifo Stack);公平竞争模式则使用先进先出队列(Fifo Queue),性能上两者是相当的,一般情况下,Fifo通常可以支持更大的吞吐量,但Lifo可以更大程度的保持线程的本地化。
这个算法的特点就是任何操作都可以根据节点的状态判断执行,而不需要用到锁。
其核心接口是Transfer,生产者的put或消费者的take都使用这个接口,根据第一个参数来区别是入列(栈)还是出列(栈)。
负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于ArrayBlockingQueue和LinkedBlockingQueue。
abstract static class Transferer<E> {
abstract E transfer(E e, boolean timed, long nanos);
}
从注释中可以看出,该类的唯一一个transfer方法是通过参数e来区分调用方法的是一个生产者线程还是一个消费者线程,如果e为null,则说明这是一个消费者线程,比如一个take操作,如果e不为null,那么就是一个生产者线程,这个数据就是这个线程需要交付的数据,比如一个put操作。
SynchronousQueue采用队列TransferQueue来实现公平性策略,采用堆栈TransferStack来实现非公平性策略,SynchronousQueue的put、take操作都是委托这两个类来实现的,我们下面先来了解一下这两个类。
最后分析的SynchronousQueue是最为复杂的阻塞队列。
生产者先进来排队,消费者来了不排队(把生产者弄出去),消费者多了排队,生产者来了不排队(把消费者弄出去)
SynchronousQueueDemo的更多相关文章
- 【JUC】JDK1.8源码分析之SynchronousQueue(九)
一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...
- 【转载】阻塞队列之三:SynchronousQueue同步队列 阻塞算法的3种实现
一.SynchronousQueue简介 Java 6的并发编程包中的SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除 ...
- java并发之SynchronousQueue实现原理
前言 SynchronousQueue是一个比较特别的队列,由于在线程池方面有所应用,为了更好的理解线程池的实现原理,笔者花了些时间学习了一下该队列源码(JDK1.8),此队列源码中充斥着大量的CAS ...
- 阻塞队列之三:SynchronousQueue同步队列 阻塞算法的3种实现
一.SynchronousQueue简介 Java 6的并发编程包中的SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除 ...
- 图解SynchronousQueue原理-公平模式
SynchronousQueue原理详解-公平模式 一.介绍 SynchronousQueue是一个双栈双队列算法,无空间的队列或栈,任何一个对SynchronousQueue写需要等到一个对Sync ...
- 阻塞队列BlockingQueue之ASynchronousQueue
一.SynchronousQueue简介 Java 6的并发编程包中的SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除 ...
- 这或许是最详细的JUC多线程并发总结
多线程进阶---JUC并发编程 完整代码传送门,见文章末尾 1.Lock锁(重点) 传统 Synchronizd package com.godfrey.demo01; /** * descripti ...
- Java多线程_JUC包下的阻塞队列
在前面我们提到了阻塞队列,也用过了LinkedBolckingQueue队列了,在这里,我们主要对 ArrayBlockingQueue,PriorityBlockingQueue,DelayQueu ...
- JUC使用
1.什么是JUC 源码 + 官方文档 面试高频问! java.util 工具包.包.分类 业务:普通的线程代码 Thread Runnable 没有返回值.效率相比入 Callable 相对较低! 2 ...
随机推荐
- NOIP-无线网路发射器选址
题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129条东西向街道和129条南北向街道所形成的网格状,并且相邻的平 ...
- require和import区别
遵循的模块化规范不一样 模块化规范:即为 JavaScript 提供一种模块编写.模块依赖和模块运行的方案.谁让最初的 JavaScript 是那么的裸奔呢——全局变量就是它的模块化规范. requi ...
- (62)Wangdao.com第十天_JavaScript 变量的作用域
在 js 中有两种作用域:全局作用域,局部作用域. 全局作用域 直接写在 <script> 标签中的变量和方法. 在网页打开时创建,在网页关闭时销毁. 全局作用域有一个全局对象 windo ...
- ECMA Script 6_解构赋值_模式匹配
解构赋值 从数组中提取值,按照对应位置,对变量赋值 只要等号右边的值不是对象或数组,就先将其转为对象. 由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错 let ...
- vue_过渡_动画
过渡效果 <style> .xxxx-enter-active, // 显示过渡 .xxxx-leave-active { // 隐藏过渡 transitio ...
- STL--set_difference
set_difference(),作用是求两个集合的差.即求A-B(属于A但不属于B的元素) set_difference()算法计算两个集合[start1, end1)和[start2, end2) ...
- [LeetCode] Minimize Max Distance to Gas Station 最小化去加油站的最大距离
On a horizontal number line, we have gas stations at positions stations[0], stations[1], ..., statio ...
- Windows下自带压缩文件工具之-makecab
在内网渗透时,当没有rar.7z等压缩工具时候,拖取文件的时候为了防止流量过大,又必须压缩把文件压缩.当然你可以自己上传一个压缩工具.Windows自带制作压缩文件工具makecb你可以了解哈.压缩单 ...
- Exception in thread "main" java.lang.IllegalStateException: Failed to read Class-Path attribute from manifest of jar file:
表示jar所在位置文件夾中沒有下載好,將目標目錄刪除,重新maven下就好了
- Spring的核心模块解析
Spring框架是一个轻量级的集成式开发框架,可以和任何一种框架集成在一起使用,可以说是一个大的全家桶.Spring从1.x发展到现在的5.x可以说是越来越强大,下面来看看Spring都包含哪些核心的 ...