• 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" ,10,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(0);
        Semaphore send = new Semaphore(1);
        Semaphore recv = new Semaphore(0);//一定先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是最为复杂的阻塞队列。
    生产者先进来排队,消费者来了不排队(把生产者弄出去),消费者多了排队,生产者来了不排队(把消费者弄出去)

  • 相关阅读:
    总结几个 webpack 打包优化的方法,前端项目必备
    vue-cli 3.0 axios 跨域请求代理配置及生产环境 baseUrl 配置
    React之MobX使用
    Couldn't load this key (OpenSSH SSH-2 private key(old PEM format))的解决办法
    HTML基础篇(一,认识HTML)
    Angular学习之路-一、配置项目
    小程序开发日志-3、调用相机竖屏拍照,并将照片转横屏显示
    自定义handsome主题默认文章头图
    DruidDataSource无限重连(mybatis数据源)
    mysql获取表字段信息(字段名,字段长度,字段类型,精度,小数点位)
  • 原文地址:https://www.cnblogs.com/yaowen/p/10774695.html
Copyright © 2020-2023  润新知