BlockingQueue---SynchronousQueue

总结

  一个特殊的 BlockingQueue 实现。  

  它并不像其他的 BlockingQueue 实现那样拥有固定的容量,而是设计用于在生产者线程和消费者线程之间直接传递元素

  本身并不存储任何元素,而是要求每一个插入操作(put 或 offer)必须等待一个相应的移除操作(take 或 poll);

特点 

  1. 直接传递SynchronousQueue 不存储任何元素,它仅仅作为生产者和消费者之间的一个传递点。
  2. 无缓冲:由于 SynchronousQueue 不能存储任何元素,所以它不能作为缓冲区来使用。这意味着,如果消费者不准备接收元素,那么生产者将被阻塞,反之亦然。
  3. 公平性:SynchronousQueue 可以选择是否采用公平模式。在公平模式下,元素的传递遵循先进先出的原则;而在非公平模式下,传递顺序不受限制。

使用场景 

  由于 SynchronousQueue 的特性,它通常用于以下场景:

  1. 即时通信:当需要在线程之间直接传递对象,并且期望尽快传递而不希望对象被缓存时。
  2. 轻量级任务队列:在某些情况下,可能不希望使用带有容量的队列来缓存任务,而是希望尽快由线程池中的线程处理这些任务。这时可以使用 SynchronousQueue 作为任务队列。
  3. 避免内存泄漏:使用 SynchronousQueue 可以避免由于队列过大而导致的内存占用问题。

方法

SynchronousQueue 提供了一些基本的方法来进行元素的传递:

  • put(e):将元素插入到队列中,如果当前没有线程准备接收这个元素,则阻塞当前线程直到有一个接收者准备好。
  • take():从队列中移除一个元素,如果当前没有线程准备插入元素,则阻塞当前线程直到有一个发送者准备好。
  • offer(e):尝试将元素插入队列,如果当前没有线程准备接收,则返回 false
  • poll(timeout, timeUnit):在指定的时间内尝试从队列中移除一个元素,如果在这段时间内没有元素被插入,则返回 null

构造函数

SynchronousQueue 提供了两个构造函数:

  • new SynchronousQueue():创建一个非公平模式的 SynchronousQueue
  • new SynchronousQueue(true):创建一个公平模式的 SynchronousQueue

示例

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
public class SynchronousQueueExample {
    public static void main(String[] args) {
 
        SynchronousQueue<Integer> queue = new SynchronousQueue<>();
 
        // 生产者线程
        Thread producerThread = new Thread(() -> {
            try {
                System.out.println("Producer: Adding element 1");
                queue.put(1);
                System.out.println("Producer: Adding element 2");
                queue.put(2);
                System.out.println("Producer: Adding element 3");
                queue.put(3);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
 
        // 消费者线程
        Thread consumerThread = new Thread(() -> {
            try {
                Integer value = queue.take();
                System.out.println("Consumer: Received element " + value);
                System.out.println("...");
            } catch (Exception e) {
                Thread.currentThread().interrupt();
            }
        });
 
        producerThread.start();
        consumerThread.start();
 
        //结果:
        //  Producer: Adding element 1
        //  Producer: Adding element 2
        //  Consumer: Received element 1
        //  ... 由于没有 消费者take,生产者被阻塞
    }
}

  

在 ThreadPoolExecutor 中的应用

  在 ThreadPoolExecutor 中,使用 SynchronousQueue 作为工作队列可以实现这样的效果:当任务提交给线程池时,如果当前没有空闲线程来执行这个任务,则提交任务的线程将被阻塞,直到有一个线程变得可用并准备执行任务。这种模式适用于希望尽快执行任务,而不希望任务被缓存的情况。

 

 

概述  

  A {@linkplain BlockingQueue blocking queue} in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa.
  A synchronous queue does not have any internal capacity, not even a capacity of one.
  You cannot {@code peek} at a synchronous queue because an element is only present when you try to remove it; you cannot insert an element (using any method) unless another thread is trying to remove it; you cannot iterate as there is nothing to iterate.
  The <em>head</em> of the queue is the element that the first queued inserting thread is trying to add to the queue; if there is no such queued thread then no element is available for removal and {@code poll()} will return {@code null}.
  For purposes of other {@code Collection} methods (for example {@code contains}), a {@code SynchronousQueue} acts as an empty collection.
  This queue does not permit {@code null} elements.    

    一个 BlockingQueue,其中每个插入操作 都必须等待另一个线程执行相应的删除操作,反之亦然。
    同步队列没有任何内部容量,甚至没有 1 的容量
    您不能在同步队列中peek,因为只有在您尝试删除元素时才存在该元素;

      你不能插入一个元素,除非另一个线程试图删除它;

      你不能迭代,因为没有什么可迭代的。
    队列头部是第一个排队的插入线程尝试添加到队列中的元素;

      如果没有这样的排队线程,则没有元素可供删除,poll()将返回 null。
    对于其他Collection的方法(例如contains),SynchronousQueue是一个空集合。
    此队列不允许 null元素;

    (SynchronousQueue实际上不是一个真正的队列,因为它不会为队列中元素维护存储空间,它只是多个线程之间数据交换的媒介

  Synchronous queues are similar to rendezvous channels used in CSP and Ada.
  They are well suited for handoff designs, in which an object running in one thread must sync up with an object running in another thread in order to hand it some information, event, or task.    

    同步队列类似于 CSP 和 Ada 中使用的集合通道。
    它们非常适合切换设计,在这种设计中,在一个线程中运行的对象必须与在另一个线程中运行的对象同步,以便将一些信息、事件或任务传递给它;

 

  This class supports an optional fairness policy for ordering waiting producer and consumer threads.
  By default, this ordering is not guaranteed.
  However, a queue constructed with fairness set to {@code true} grants threads access in FIFO order.

    

    此类支持可选的公平性策略,用于对等待的生产者和使用者线程进行排序。
    默认情况下,不保证此排序。
    但是,在公平性设置为 {@code true} 的情况下构造的队列按 FIFO 顺序授予线程访问权限;

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
public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
 
        abstract static class Transferer<E> {
            /**
             *
             * @param e
             *          if non-null, the item to be handed to a consumer;                       如果e非null,item将被交给consumer
             *          if null, requests that transfer return an item offered by producer.     如果e是null,producer提供的item将会返回
             * @param timed
             *          if this operation should timeout
             * @param nanos
             *          the timeout, in nanoseconds
             * @return  if non-null, the item provided or received;
             *          if null,the operation failed due to timeout or interrupt -- the caller can distinguish which of these occurred by checking Thread.interrupted.
             */
            abstract E transfer(E e, boolean timed, long nanos);
        }
 
        static final class TransferQueue<E> extends Transferer<E> {
 
        }
 
        static final class TransferStack<E> extends Transferer<E> {
 
        }
 
 
        private transient volatile Transferer<E> transferer;
 
        public SynchronousQueue() {
            this(false);
        }
 
        public SynchronousQueue(boolean fair) {
            transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
        }
         
    }

  

 

应用场景

  如果我们不确定来自生产者请求数量,但是这些请求需要很快的处理掉,那么配合SynchronousQueue为每个生产者请求分配一个消费线程是最处理效率最高的办法。    

  Executors.newCachedThreadPool()底层就使用了SynchronousQueue,这个线程池根据需求创建新的线程;

 

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public static void main(String[] args) {
        final BlockingQueue<Integer> synchronousQueue = new SynchronousQueue<>();
 
        SynchronousQueueConsumer queueConsumer1 = new SynchronousQueueConsumer(synchronousQueue);
        new Thread(queueConsumer1, "queueConsumer1").start();
 
        SynchronousQueueProducer queueProducer = new SynchronousQueueProducer(synchronousQueue);
        new Thread(queueProducer, "queueProducer").start();
    }
 
 
    static class SynchronousQueueProducer implements Runnable {
 
        private final BlockingQueue<Integer> blockingQueue;
        public SynchronousQueueProducer(BlockingQueue<Integer> queue) {
            this.blockingQueue = queue;
        }
 
        @Override
        public void run() {
            int i = 0;
            boolean flag = true;
            while (flag) {
                try {
                    boolean offer = blockingQueue.offer(i, 1, TimeUnit.SECONDS);
                    System.out.println(Thread.currentThread().getName() + " offer(): " + ++i + " " + offer);
                    if (i >= 100){
                        flag = false;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
 
    }
 
    static class SynchronousQueueConsumer implements Runnable {
        private final BlockingQueue<Integer> blockingQueue;
 
        public SynchronousQueueConsumer(BlockingQueue<Integer> queue) {
            this.blockingQueue = queue;
        }
 
        @Override
        public void run() {
            boolean flag = true;
            while (flag) {
                try {
                    Integer data = blockingQueue.poll(1, TimeUnit.SECONDS);
                    System.out.println(Thread.currentThread().getName() + " poll(): " + data);
                    if(null == data || data >= 100){
                        flag = false;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
 
    }
 
结果:
queueProducer offer(): 1 true
queueConsumer1 poll(): 0
queueConsumer1 poll(): 1
queueProducer offer(): 2 true
queueProducer offer(): 3 true
queueConsumer1 poll(): 2
queueConsumer1 poll(): 3
queueProducer offer(): 4 true
queueProducer offer(): 5 true
queueConsumer1 poll(): 4
queueConsumer1 poll(): 5
queueProducer offer(): 6 true
queueProducer offer(): 7 true
queueConsumer1 poll(): 6
queueConsumer1 poll(): 7
queueProducer offer(): 8 true
queueProducer offer(): 9 true
queueConsumer1 poll(): 8
queueConsumer1 poll(): 9
queueProducer offer(): 10 true

  

链路

offer(E)

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
/**
     *
     * @param e the element to add
     * @return  true if the element was added to this queue, else false
     */
    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        return transferer.transfer(e, true, 0) != null;
    }
 
    // java.util.concurrent.SynchronousQueue.offer(E, long, java.util.concurrent.TimeUnit)
    /**
     * Inserts the specified element into this queue, waiting if necessary up to the specified wait time for another thread to receive it.
     *  等待一定的时间,让另一个线程消费queue的元素
     * @return
     * @throws InterruptedException
     */
    public boolean offer(E e, long timeout, TimeUnit unit)
            throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
            return true;
        if (!Thread.interrupted())
            return false;
        throw new InterruptedException();
    }
 
    // java.util.concurrent.SynchronousQueue.TransferStack.transfer
    E transfer(E e, boolean timed, long nanos) {
 
    }

  

poll()

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
/**
     * Retrieves and removes the head of this queue, if another thread is currently making an element available.
     *  如果另一个线程生产一个可用的元素 -> 查询&移除 queue的head
     * @return the head of this queue, or null if no element is available
     */
    public E poll() {
        return transferer.transfer(null, true, 0);
    }
 
    // java.util.concurrent.SynchronousQueue.poll(long, java.util.concurrent.TimeUnit)
    /**
     * Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time, for another thread to insert it.
     *  等待一定的时间,让另一个线程insert item,检索&移除queue中的item
     * @return
     * @throws InterruptedException
     */
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E e = transferer.transfer(null, true, unit.toNanos(timeout));
        if (e != null || !Thread.interrupted())
            return e;
        throw new InterruptedException();
    }
 
    // java.util.concurrent.SynchronousQueue.TransferStack.transfer
    E transfer(E e, boolean timed, long nanos) {
 
    }

  

 

posted on   anpeiyong  阅读(6)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示