多线程基础学习 渣華学习成长记录

2019-05-18 阻塞队列和并发容器

2019-05-27  本文已影响0人  zecan

1.什么是阻塞队列

阻塞队列(BlockingQueue)是指当队列为空时,取出元素的操作会一直阻塞,直到队列有元素。当队列满时,插入元素会阻塞,直到队列恢复非满可用状态。

2.java里的阻塞队列

JDK提供7个阻塞队列,分别是:

下面介绍几种常见的阻塞队列:
ArrayBlockingQueue
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。我们可以使用以下代码创建一个公平的阻塞队列:

  ArrayBlockingQueue fairQueue = new  ArrayBlockingQueue(1000,true);

LinkedBlockingQueue
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE,此队列按照先进先出的原则对元素进行排序。

PriorityBlockingQueue
PriorityQueue是一个支持优先级的无界队列。默认情况下元素采取自然排序,也可以通过比较器comparator来执行元素的排序规则。

DelayQueue
DelayQueue是一个支持演示获取元素的无界阻塞队列。队列使用PriorityQueue来实现。队列中的元素必须实现Delay接口,在创建元素时可以指定多久才能从队列中获取当前的元素。只有延时期满的元素才能从队列中获取。我们可以将DelayQueue运用在以下应用场景:

package com.demo.contrainer;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueTest {
    
    class DelayEle implements Delayed{
        public long delayTime;
        public String name;
        public DelayEle (int delayTime, String name){
            this.delayTime = System.currentTimeMillis() + delayTime;
            this.name = name;
        }

        public int compareTo(Delayed o) {
            return this.delayTime  - ((DelayEle)o).delayTime >  0 ? 1 :
                            this.delayTime  - ((DelayEle)o).delayTime < 0  ? -1 :0;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.delayTime  - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
        }
        public String toString(){
            return this.name;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        DelayQueueTest t = new DelayQueueTest();
        //这里第一个参数是在多少毫秒才能被取出
        DelayEle ele1 = t.new DelayEle(1000,"t1");
        DelayEle ele2 = t.new DelayEle(5000,"t2");
        DelayEle ele3 = t.new DelayEle(3000,"t3");
        
        DelayQueue dq = new DelayQueue();
        dq.add(ele1);
        dq.add(ele2);
        dq.add(ele3);
        
        while(dq.size() > 0){
            System.out.println(dq.take());
        }
    }
    
}

3.阻塞队列中的方法 VS 非阻塞队列中的方法

1.非阻塞队列中的几个主要方法:

  add(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;

  remove():移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;

  offer(E e):将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false;

  poll():移除并获取队首元素,若成功,则返回队首元素;否则返回null;

  peek():获取队首元素,若成功,则返回队首元素;否则返回null

对于非阻塞队列,一般情况下建议使用offer、poll和peek三个方法,不建议使用add和remove方法。因为使用offer、poll和peek三个方法可以通过返回值判断操作成功与否,而使用add和remove方法却不能达到这样的效果。注意,非阻塞队列中的方法都没有进行同步措施。

2.阻塞队列中的几个主要方法:
阻塞队列包括了非阻塞队列中的大部分方法,上面列举的5个方法在阻塞队列中都存在,但是要注意这5个方法在阻塞队列中都进行了同步措施。除此之外,阻塞队列提供了另外4个非常有用的方法:

  put(E e)

  take()

  offer(E e,long timeout, TimeUnit unit)

  poll(long timeout, TimeUnit unit)

4.并发容器 copy-on-write(COW)

1.CopyOnWirte容器即写时复制的容器,就是当容器添加元素的时候,先将容器复制一份,然后往新的容器里面添加元素,然后再将原容器指向新容器。JDK1.5以后,java并发包提供了两个使用CopyOnWrite机制实现的并发容器:CopyOnWriteArrayList和CopyOnWriteArraySet。
2.优点:容器支持并发的读,不需要加锁。适用于读多写少的并发场景。
3.缺点:内存占用问题以及无法保证数据一致性。

5.并发容器ConcurrentHashMap

1.锁分段技术:ConCurrentHashMap容器里有多把锁,每一把锁用于锁容器其中一部分数据,当多线程范文容器中不同数据段的数据是,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率。
2.ConcurrentHashMap的分段segment数组长度必须是2的n次方,这样的好处是方便采用移位操作来进行hash,加快hash的过程。
3.Segment的数据结构:

static final class Segment<K,V> extends ReentrantLock implements Serializable {
    transient volatile int count;
    transient int modCount;
    transient int threshold;
    transient volatile HashEntry<K,V>[] table;
    final float loadFactor;
}

Segment里面的成员变量的意义:

count:Segment中元素的数量
modCount:对table的大小造成影响的操作的数量(比如put或者remove操作)
threshold:阈值,Segment里面元素的数量超过这个值依旧就会对Segment进行扩容
table:链表数组,数组中的每一个元素代表了一个链表的头部
loadFactor:负载因子,用于确定threshold
4.ConcurrentHashMap的get操作是不用加锁的,put操作是加锁完成的.

上一篇 下一篇

猜你喜欢

热点阅读