并发容器类

HashMap基本原理

hashMap 是一个以键值对存储的数据结构,其内部采用链表存储数据,key按照hash算法再取模得到数据存放的位置

JDK1.7数据结构图
HashMap jdk1.7

内部存储数据就是数组加链表,数组的位置检索通过hash算法与数组长度取模得到,链表则直接采用循环next找下一个来检索数据

  1. 初始化HashMap时,默认创建一个固定长度的数组
  2. 每次put或get数据时,会先根据key使用hash算法得到一个key对应的值,再用hash对当前数组长度取模得到当前key所在位置
  3. 读取数据时,根据位置,循环当前数组元素下的链表
  4. 存储数据时,同样根据所在位置,将数据存放在当前数组索引内
  5. 每个链表的数据超过一定数量后,将会重新创建一个新的数组,进行扩容
    扩容会按照当前数组长度成倍扩充,然后将老的数组中的数据循环遍历,重新计算hash按照新数组长度取模存储至新的数组之中
JDK1.8 数据结构图 - 新增红黑树
HashMap jdk1.8

与JDK1.7的区别在于,每个链表数据超过8时,将采用新的算法红黑树数据结构存储数据

jdk1.8中的红黑树主要是解决,当链表过长的时候,每次循环检索数据会过长,导致性能低下问题


HashTable

  • HashTable与HashMap原理大致相同,但是最重要的一点是它是线程安全的。
  • 每次数据的读取都是由synchronized 控制
  • HashTable虽然保证的线程的安全,但效率很低,影响性能,JDK中不建议使用

ConcurrentHashMap基本原理

ConcurrentHashMap是一个以键值对存储的数据结构,是一个线程安全的集合类,它采用分段锁的方式对每个分片进行加解锁,实现不同线程间操作不同分片上数据互不干扰的模式。JDK1.7与JDK1.8存储数据有很大的区别

JDK1.7数据结构图
ConcurrentHashMap jdk1.7
  • ConcurrentHashMap创建时会有固定的长度数组,数组中存储的是一个引用
  • 内部实现了一个特殊的HashTable(类似HashTable,但此HashTable非彼HashTable),分段锁来保证线程安全
  • 但JDK1.7中引用的数组长度是固定的,并不会扩容,扩容的只在分片内部的HashTable上,所以加锁的数据范围会很大,导致在高并发的情况下,性能低下
JDK1.8数据结构图
ConcurrentHashMap jdk1.8

JDK1.8中,它与HashMap类似,当数据长度超长时也会扩容,当链表长度大于8的时候,数据结构也会转换成红黑树

  • ConcurrentHashMap 由CAS操作和synchronized来保证线程安全
    添加数据时如果当前索引位置为NULL,则直接采用CAS(NULL,新的值)来存储对象
    如果不为NULL,则将通过synchronized给当前的链表加上锁,再操作数据
  • ConcurrentHashMap JDK1.8解决了1.7中分段锁的力度过大,锁数据过多的问题

ConcurrentSkipListMap

ConcurrentSkipListMap是线程安全的有序的哈希表,其中key是有序的,采用跳表的数据存储方式,它适用于高并发的场景

ConcurrentSkipListMap数据结构
ConcurrentSkipListMap
  • 查找数据的时候,都是从headIndex开始,然后根据它指向的索引条件判断查找
  • 数据都是根据key有序存储
  • ConcurrentSkipListMap的分层数是通过一个随机数生成算法来确定

集合中 List 与 Set 的最大区别在于数据重复性

  • ArrayList 内部采用数组的方式存储数据,扩容的机制根据当前数组容量是否存满,则拷贝数组到新的数组的方式。
  • CopyOnWriteArrayList 与ArrayList类似,区别在于每次新增一条数据就会拷贝整个数组到新的数组,再切换引用。是线程安全的
  • HashSet 内部基于HashMap实现,无序且不可重复
  • CopyOnWriteArraySet 基于CopyOnWriteArrayList 实现,是线程安全的
  • ConcurrentSkipListSet 基于ConcurrentSkipListMap实现,数据不可重复,有序集合,它只用到了ConcurrentSkipListMap中的key,线程安全

Queue API

Queue分为阻塞队列和非阻塞队列

方法 作用 描述
add 添加一个元素 如果队列已满,抛异常
remove 移除并返回队列头部元素 如果队列为空,抛异常
element 返回队列头部元素 如果队列为空,抛异常
-- -- --
offer 添加一个元素并返回true 如果队列已满,返回false
poll 移除并返回队列头部元素 如果队列为空,返回null
peek 返回队列头部元素 如果队列为空,返回null
-- -- --
put 添加一个元素 如果队列已满,阻塞
take 移除并返回队列头部元素 如果队列为空,阻塞

ArrayBlockingQueue 数组等待队列

内部存储数据采用数组的方式,通过ReentrantLock锁实现阻塞队列,创建时需要定义长度

    public static void testArrayBlockingQueue() throws InterruptedException {
        ArrayBlockingQueue queue = new ArrayBlockingQueue(12);

        queue.offer("");        //添加到队尾,        不阻塞
        queue.put("");
        queue.poll();              //取出头部,并移除,  不阻塞
        queue.peek();              //取出头部,不移除,  不阻塞
 
        try {
            queue.put("");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            queue.take();       //获取队列头部,并移除   无元素时,阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    } 

LinkedBlockingQueue 链表等待队列

内部采用链表结构存储数据,通过两把锁实现的阻塞队列,创建时无需定义长度

    public static void testLinkedBlockingQueue(){
        /*
           有界队列、无界队列
         */
        LinkedBlockingQueue linkedQueue = new LinkedBlockingQueue();
        linkedQueue.offer("");
        linkedQueue.poll();

        try {
            linkedQueue.put("");
            linkedQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

ConcurrentLinkedQueue 高并发链表队列

内部采用链表结构存储数据,通过CAS方式来实现队列,是一个非阻塞的队列

   public void test03_ConcurrentLinkedQueue(){
        //非阻塞的度列
        ConcurrentLinkedQueue conQueue = new ConcurrentLinkedQueue<>();
        conQueue.offer("");
        conQueue.poll();

        //conQueue.put("");
        //conQueue.take();
    } 

SynchronousQueue 同步队列

内部不存储数据,阻塞队列;它向一道门,开门即出,而以上的队列类似一个胡同

/*
1、take会阻塞,直到取到元素
2、put时会阻塞,直到被get
3、若没有take方法阻塞等待,offer的元素可能会丢失
4、poll取不到元素,就返回null,如果正好有put被阻塞,可以取到
5、peek 永远只能取到null,不能让take结束阻塞
 */

public class DemoSyncQueueTest {
    static SynchronousQueue syncQueue = new SynchronousQueue<>();

    //put时会阻塞,直到被get
    public static void test01() throws InterruptedException {
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(3000L);
                    System.out.println(syncQueue.poll());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        System.out.println("begain to put...");
        syncQueue.put("put_element");
        System.out.println("put done...");
    }

    //3、若没有take方法阻塞等待,offer的元素可能会丢失
    public static void test02() throws InterruptedException {
        syncQueue.offer("offered_element");

        System.out.println(syncQueue.poll());
    }

    //4、poll取不到元素,就返回null,如果正好有put被阻塞,可以取到
    public static void test03() throws InterruptedException {
/*        new Thread(){
            @Override
            public void run() {
                try {
                    syncQueue.put("put_element");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();*/

        Thread.sleep(200L);
        Object obj =  syncQueue.poll();
        System.out.println(obj);
    }

    //peek 永远只能取到null,不能让take结束阻塞
    public static void test04() throws InterruptedException {
        new Thread(){
            @Override
            public void run() {
                try {
                    syncQueue.put("put_element");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        Thread.sleep(200L);
        Object obj =  syncQueue.peek();
        System.out.println(obj);
    }

    public static void main(String args[]) throws InterruptedException {
        test02();
    }
}

PriorityBlockingQueue 优先级等待队列

内部存储数据采用数组的方式,通过ReentrantLock锁实现阻塞队列,可以实现队列的优先级规则

public class DemoPriorityBlockingQueue {
    public static void main(String args[]){
        // 自己定义队列的优先级
        PriorityBlockingQueue queue = new PriorityBlockingQueue<>(5, new Comparator() {
            @Override
            public int compare(Student o1, Student o2) {
                int num1 = o1.age;
                int num2 = o2.age;

                if (num1 > num2)
                    return 1;
                else if (num1 == num2)
                    return 0;
                else
                    return -1;
            }
        });
        queue.put(new Student(10, "enmily"));
        queue.put(new Student(20, "Tony"));
        queue.put(new Student(5, "baby"));

        for (;queue.size() >0;){
            try {
                System.out.println(queue.take().name);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Student{
    public int age;
    public String name;

    public Student(int age, String name){
        this.age = age;
        this.name = name;
    }
}

你可能感兴趣的:(并发容器类)