阻塞队列

阻塞队列

在线程池中我们可以看到他们使用的场景,以最常用的三种线程池来看,线程池可以查看之前写的文章。

        ExecutorService executorService1 = Executors.newFixedThreadPool(10);
        ExecutorService executorService2 = Executors.newCachedThreadPool();
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();

 

 

 

 

定长:fix          linkedBlockingQueue

single:单一    linkedBlockingQueue

cache:变长    synchonousQueue

在框架底层:mq(kafka,rabbitmq,rocketmq),nacos(eruka)等等也都有使用阻塞队列。

队列都实现了接口 BlockingQueue

它提供了一些队列的基本操作,比如:添加操作 add              put        offer(e,time,unit)        offer,取元素操作:remove        take        poll ,同一个操作多个方法有是时候傻傻分不清有什么不同。

可以参考如下列表

                      抛异常            阻塞          超时                          返回特殊值(boolean,null)

插入                  add              put        offer(e,time,unit)        offer

取元素              remove        take        poll                              poll  

查询元素          element        -           -                                       peek

 

 ArrayBlockingQueue

1:基于数组实现的阻塞队列,可以实现FIFO,可以作为生产者和消费者

提供了三个构造函数,但是都必须传入一个int数据用来指定队列容量这和LinkedBlockingQueue不一样,后面会看到。

  

 

 

 以第二个构造函数为例,第一个构造函数也是调用第二个构造函数,第二个参数默认为false,

/**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and the specified access policy.
     *
     * @param capacity the capacity of this queue   队列容量
     * @param fair if {@code true} then queue accesses for threads blocked
     *        on insertion or removal, are processed in FIFO order;
     *        if {@code false} the access order is unspecified.  如果为true可以让队列在被线程访问的时候保证FIFO,靠new ReentrantLock(fair)公平锁实现
* @throws IllegalArgumentException if {@code capacity < 1} */

public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0) throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
/** Condition notEmpty; Condition for waiting takes */
// 上面我们说的 take数据的时候,如果队列为空利用这个条件对象进行阻塞
notEmpty = lock.newCondition();
/** Condition notFull ; Condition for waiting puts*/
//
上面我们说的 put数据的时候,如果队列满了利用这个条件对象进行阻塞
   notFull = lock.newCondition(); 
}

 

当队列空的时候take阻塞,notEmpty.await()

 

 当有元素入队的时候就会唤醒

当队列满的时候put阻塞,notFull.await()

 

 当有元素出队的时候就会唤醒:notFull.signal()

 

 上面的睡眠和唤醒和await,notify很像,但是他们有很大的区别:

synchronized :自动释放锁
 Object 中的方法:wait   notify  notifyAll(synchronized作用范围内使用)
 notify是随机唤醒一个线程
 ReentrantLock:手动加锁,非公平锁1,公平锁0(所有的线程都有机会拿到锁),响应中断
 解锁必须放在finally里面
Condition:await  signal   signalAll(ReentrantLock作用范围内使用)
Condition通过Lock获取的newCondition,获取多次
newCondition返回的对象await 必须用同一个对象signal

 

从构造器也可以看出,全局只有一把锁 lock,也就说入队和出队用的同一把锁,出队和入队相互阻塞。

还有一点需要留意,在第三种带集合参数的构造器里用了加锁操作,这和指令重排有关系。

我们知道创建一个对象要经过三个过程:1)分配内存  2)初始化数据  3)  内存地址赋值给引用     2,3步骤可能会发生指令重排。

为了防止指令重排,可以加关键字 violatile,加锁锁,对象使用 final 修饰。 

 

而且它的迭代器是线程安全的,我们知道ArrayList不是线程安全,当然它的迭代器也不是线程安全的,但是ArrayBlockingQueue的迭代器是线程安全的,迭代器 是弱一致性   读到的数据不一定准   操作过的元素不会立马体现,

 

 每次创建的迭代器都会注册到Itrs中,这样再迭代过程中某个迭代器对队列的修改,就可以通知其他迭代器知道。

 

 

LinkedBlockingQueue 

 基于节点的数据结构,而且只是单向链表,而且默认的队列长度是Integer.Max_VALUE,可以认为是无界的队列。阻塞,FIFO。

 

 但是它的阻塞和ArrayBlockingQueue不同,ArrayBlockingQueue出队和入队用同一个锁,相互阻塞,LinkedBlockingQueue 出队和入队用不同的锁,互不干扰。

 在出队时候有一个需要留意下,也是我们值得学习的地方。

/**
     * Removes a node from head of queue.
     *
     * @return the node
     */
    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head;
        Node<E> first = h.next;
// 有这一步操作,是为了让
h 失去引用,完全独立 让GC快速回收
h.next = h; // help GC head = first; E x = first.item; first.item = null; return x; }

 

SynchronousQueue

有人称为无缓冲队列,它不存储元素,只用来阻塞线程,在cache线程池中的时候使用这个队列。我们在刚开始的看到cache线程池的构造函数中,是没有核心线程数的,而非核心线程数确实Integer的最大值

它内部没有数组和节点来存储元素的。

 

 我们看构造函数,有一个fair的参数,但是这里的公平非公平不是真正意义上的锁的公平非公平,而是线程的执行的先后,因为这个队列是用来阻塞线程的,我们看到如果为true是TransferQueue,否则是TransferStack。也就是为true的话 线程是FIFO,否则的话就是LIFO.

?

 

 所谓的出队和入队也是靠Transer**来操作的,当一个线程操作put(E)操作的时候,如果队列里没有其它出队线程(比如take)处于阻塞状态,那么这个put操作的线程也会处于阻塞状态,直到有个出队线程执行出队操作,这两个线程就会同时返回。如果只有一个出队线程来操作也会处于阻塞状态,直到有个入队的线程来操作,线程才会返回。

 

posted @ 2021-03-25 20:19  蒙恬括  阅读(105)  评论(0编辑  收藏  举报