Java中的阻塞队列(五) --无容量队列SynchronousQueue

参考文章:https://www.cnblogs.com/leesf456/p/5560362.html

二:构造函数
2.1 无参构造

可以看出默认实现的是非公平策略

    public SynchronousQueue() {
        this(false);
    }
2.2 公平策略

非公平策略采用TransferStack内部类实现,TransferQueue实现公平策略

    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }
三: 非公平内部类TransferStack

SynchronousQueue阻塞队列采用两种内部类数据结构实现公平与非公平的策略,其中TransferStack是默认的策略非公平实现,TransferQueue是公平策略实现

3.1 重要属性
        // 表示消费数据的消费者,take、poll
        static final int REQUEST    = 0;
        // 表示生产数据的生产者,offer、put
        static final int DATA       = 1;
        // 表示匹配另一个生产者或消费者
        static final int FULFILLING = 2;
        // 头结点
        volatile SNode head;
3.2 内部类SNode

SNode内部类属性包含下一节点、匹配节点、等待线程、实例数据、节点模式等信息

       volatile SNode next;        // next node in stack
       volatile SNode match;       // the node matched to this
       volatile Thread waiter;     // to control park/unpark
       Object item;                // data; or null for REQUESTs
       int mode;

tryMatch()是将节点s与本节点进行匹配

  1. 本节点匹配域为空切CAS替换匹配节点成功,就释放等待线程
  2. 不满足上述条件则查看本节点等待与SNode是否与本次匹配节点s相等
        boolean tryMatch(SNode s) {
            if (match == null &&
                UNSAFE.compareAndSwapObject(this, matchOffset, null, s)) {
                Thread w = waiter;
                if (w != null) {    // waiters need at most one unpark
                    waiter = null;
                    LockSupport.unpark(w);
                }
                return true;
            }
            return match == s;
        }
3.3 transfer
	E transfer(E e, boolean timed, long nanos) {
        
            SNode s = null; 
            // 根据e确定此次转移的模式(是put or take)
            int mode = (e == null) ? REQUEST : DATA;
    
            for (;;) {
                SNode h = head;
                // 头结点为null或者头结点的模式与此次转移的模式相同
                if (h == null || h.mode == mode) {  
                	// 设置了timed并且等待时间小于等于0,表示不能等待,需要立即操作   
                    if (timed && nanos <= 0) { 
                    	// 头结点不为null并且头结点被取消    
                        if (h != null && h.isCancelled()) 
                        	// 重新设置头结点(弹出之前的头结点)    
                            casHead(h, h.next); 
                        else 
                            return null;
                    }
                    // 生成一个SNode结点;将原来的head头结点设置为该结点的next结点;将head头结点设置为该结点 
                    else if (casHead(h, s = snode(s, e, h, mode))) { 
                        // 空旋或者阻塞直到s结点被FulFill操作所匹配
                        SNode m = awaitFulfill(s, timed, nanos);
                         // 匹配的结点为s结点(s结点被取消)
                        if (m == s) {              
                            // 清理s结点
                            clean(s);
                            return null;
                        }
                        // h重新赋值为head头结点,并且不为null;头结点的next域为s结点,表示有结点插入到s结点之前,完成了匹配
                        if ((h = head) != null && h.next == s) 
                            // 比较并替换head域(移除插入在s之前的结点和s结点)
                            casHead(h, s.next);   
                        // 根据此次转移的类型返回元素
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    }
                } 
                 // 没有FULFILLING标记,尝试匹配
                else if (!isFulfilling(h.mode)) {
                    if (h.isCancelled())
                        // 比较并替换head域(弹出头结点)
                        casHead(h, h.next);        
                    else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { 
                        for (;;) { 
                            SNode m = s.next;      
                            if (m == null) {
                                // 比较并替换head域
                                casHead(s, null);  
                                s = null;          
                                break;     
                            }
                            // m结点的next域
                            SNode mn = m.next;
                            if (m.tryMatch(s)) {
                                // 比较并替换head域(弹出s结点和m结点)
                                casHead(s, mn);    
                                // 根据此次转移的类型返回元素
                                return (E) ((mode == REQUEST) ? m.item : s.item);
                            } else
                                // 比较并替换next域(弹出m结点)
                                s.casNext(m, mn); 
                        }
                    }
                } else { // 头结点正在匹配                            
                    // 保存头结点的next域
                    SNode m = h.next; 
                    if (m == null) 
                        // 比较并替换head域(m被其他结点匹配了,需要弹出h)
                        casHead(h, null);          
                    else { 
                        SNode mn = m.next;
                        if (m.tryMatch(h)) 
                            // 比较并替换head域(弹出h和m结点)
                            casHead(h, mn);     
                        else            
                            h.casNext(m, mn);    
                    }
                }
            }
        }
3.4 awaitFulfill

此函数表示当前线程自旋或阻塞,直到结点被匹配。awaitFulfill函数调用了shouldSpin函数

SNode awaitFulfill(SNode s, boolean timed, long nanos) {
            // 根据timed标识计算截止时间
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            // 获取当前线程
            Thread w = Thread.currentThread();
            // 根据s确定空旋等待的时间
            int spins = (shouldSpin(s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0); 
            for (;;) { // 无限循环,确保操作成功
                if (w.isInterrupted()) // 当前线程被中断
                    // 取消s结点
                    s.tryCancel();
                // 获取s结点的match域
                SNode m = s.match;
                // m不为null,存在匹配结点
                if (m != null)
                    // 返回m结点
                    return m;
                if (timed) { 
                    // 确定继续等待的时间
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) { // 继续等待的时间小于等于0,等待超时
                        // 取消s结点
                        s.tryCancel();
                        // 跳过后面的部分,继续
                        continue;
                    }
                }
                if (spins > 0) // 空旋等待的时间大于0
                    // 确实是否还需要继续空旋等待
                    spins = shouldSpin(s) ? (spins-1) : 0;
                else if (s.waiter == null) // 等待线程为null
                    // 设置waiter线程为当前线程
                    s.waiter = w; // establish waiter so can park next iter
                else if (!timed) // 没有设置timed标识
                    // 禁用当前线程并设置了阻塞者
                    LockSupport.park(this);
                else if (nanos > spinForTimeoutThreshold) // 继续等待的时间大于阈值
                    // 禁用当前线程,最多等待指定的等待时间,除非许可可用
                    LockSupport.parkNanos(this, nanos);
            }
        }
3.5 shouldSpin

当前结点所包含的线程(当前线程)进行空旋等待,有如下情况需要进行空旋等待

  1. 当前结点为头结点
  2. 头结点为null
  3. 头结点正在匹配中
boolean shouldSpin(SNode s) {
            // 获取头结点
            SNode h = head;
            // s为头结点或者头结点为null或者h包含FULFILLING标记,返回true
            return (h == s || h == null || isFulfilling(h.mode)); 
        }
3.6 clean

此函数用于移除从栈顶头结点开始到该结点(不包括)之间的所有已取消结点

void clean(SNode s) {
            // s结点的item设置为null
            s.item = null;   // forget item
            // waiter域设置为null
            s.waiter = null; // forget thread
            // 获取s结点的next域
            SNode past = s.next;
            if (past != null && past.isCancelled()) // next域不为null并且next域被取消
                // 重新设置past
                past = past.next;

            // Absorb cancelled nodes at head
            SNode p;
            while ((p = head) != null && p != past && p.isCancelled()) // 从栈顶头结点开始到past结点(不包括),将连续的取消结点移除
                // 比较并替换head域(弹出取消的结点)
                casHead(p, p.next);

            // Unsplice embedded nodes
            while (p != null && p != past) { // 移除上一步骤没有移除的非连续的取消结点
                // 获取p的next域
                SNode n = p.next;
                if (n != null && n.isCancelled()) // n不为null并且n被取消
                    // 比较并替换next域
                    p.casNext(n, n.next);
                else
                    // 设置p为n
                    p = n;
            }
        }
四:示例解说公平实现TransferQueue
4.1 示例代码
package com.hust.grid.leesf.collections;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>(true);
        Producer p1 = new Producer("p1", queue, 10);
        Producer p2 = new Producer("p2", queue, 50);
        
        Consumer c1 = new Consumer("c1", queue);
        Consumer c2 = new Consumer("c2", queue);
        
        c1.start();
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        c2.start();
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        p1.start();
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        p2.start();
        
    }

    static class Producer extends Thread {
        private SynchronousQueue<Integer> queue;
        private int n;
        public Producer(String name, SynchronousQueue<Integer> queue, int n) {
            super(name);
            this.queue = queue;
            this.n = n;
        }
        
        public void run() {
            System.out.println(getName() + " offer result " + queue.offer(n));
        }
    }
    
    static class Consumer extends Thread {
        private SynchronousQueue<Integer> queue;
        public Consumer(String name, SynchronousQueue<Integer> queue) {
            super(name);
            this.queue = queue;
        }
        
        public void run() {
            try {
                System.out.println(getName() + " take result " + queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
4.2 运行结果
p1 offer result true
c1 take result 10
p2 offer result true
c2 take result 50
4.3 结果分析

从运行结果可知,c1线程会比c2线程先匹配(因为采用公平策略,先入队列先匹配,所以c1先得到匹配,然后再匹配c2)

五:重点方法实现
// 将指定元素添加到此队列,如有必要则等待另一个线程接收它
    public void put(E e) throws InterruptedException {
        // e为null则抛出异常
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, false, 0) == null) { // 进行转移操作
            // 中断当前线程
            Thread.interrupted();
            throw new InterruptedException();
        }
    }
    
    // 将指定元素插入到此队列,如有必要则等待指定的时间,以便另一个线程接收它
    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        // e为null则抛出异常
        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();
    }
    
    // 如果另一个线程正在等待以便接收指定元素,则将指定元素插入到此队列
    public boolean offer(E e) {
        // e为null则抛出异常
        if (e == null) throw new NullPointerException();
        return transferer.transfer(e, true, 0) != null; // 进行转移操作
    }
    
    // 获取并移除此队列的头,如有必要则等待另一个线程插入它
    public E take() throws InterruptedException {
        // 进行转移操作
        E e = transferer.transfer(null, false, 0);
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }
    
    // 获取并移除此队列的头,如有必要则等待指定的时间,以便另一个线程插入它
    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E e = transferer.transfer(null, true, unit.toNanos(timeout));
        if (e != null || !Thread.interrupted()) // 元素不为null或者当前线程没有被中断
            return e;
        throw new InterruptedException();
    }
    
    // 如果另一个线程当前正要使用某个元素,则获取并移除此队列的头
    public E poll() {
        return transferer.transfer(null, true, 0);
    }
    
    // 始终返回 true
    public boolean isEmpty() {
        return true;
    }
    
    // 始终返回 0
    public int size() {
        return 0;
    }
    
    // 始终返回 0
    public int remainingCapacity() {
        return 0;
    }
    
    // 不执行任何操作
    public void clear() {
    }
    
    // 始终返回false
    public boolean contains(Object o) {
        return false;
    }
    
    // 始终返回false
    public boolean remove(Object o) {
        return false;
    }
    
    // 除非给定 collection 为空,否则返回 false
    public boolean containsAll(Collection<?> c) {
        return c.isEmpty();
    }
    
    // 始终返回 false
    public boolean removeAll(Collection<?> c) {
        return false;
    }
    
    // 始终返回 false
    public boolean retainAll(Collection<?> c) {
        return false;
    }
    
    // 始终返回 null
    public E peek() {
        return null;
    }
    
    // 返回一个空迭代器,其中 hasNext 始终返回 false
    public Iterator<E> iterator() {
        return Collections.emptyIterator();
    }
    
    // 
    public Spliterator<E> spliterator() {
        return Spliterators.emptySpliterator();
    }
    
    // 返回一个 0 长度的数组
    public Object[] toArray() {
        return new Object[0];
    }
    
    // 将指定数组的第 0 个元素设置为 null(如果该数组有非 0 的长度)并返回它
    public <T> T[] toArray(T[] a) {
        if (a.length > 0)
            a[0] = null;
        return a;
    }
    
    // 移除此队列中所有可用的元素,并将它们添加到给定 collection 中
    public int drainTo(Collection<? super E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        int n = 0;
        for (E e; (e = poll()) != null;) {
            c.add(e);
            ++n;
        }
        return n;
    }
    
    // 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中
    public int drainTo(Collection<? super E> c, int maxElements) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        int n = 0;
        for (E e; n < maxElements && (e = poll()) != null;) {
            c.add(e);
            ++n;
        }
        return n;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值