1.3.3 并发容器类MAP/LIST/SET/QUEUE

HashMap

下标计算方法:hashCode & (length-1) ,&按位与操作,二进制相同位都是1,该位才为1

JDK1.7与JDK1.8中HashMap区别: JDK1.8在put元素时,若计算得到的下标下的链表长度达到8时(算上当前要加到链表尾部的元素),链表会转换成红黑树提高查找效率

通过调整hashmap初始大小避免rehash的实例:要转移1000个数据到hashmap,那么假定初始大小1024,在转移到1024*0.75=768个时会触发rehash,将1024扩容到2048,所以如果在设置初始大小时指定2048就可以避免一次rehash

https://www.cnblogs.com/williamjie/p/9358291.html

hashMap是线程不安全的,hashtable利用synchronized加在put和get方法上实现线程安全

ConcurrentHashMap

线程安全的HashMap

JDK1.7 ConcurrentHashMap有多个HashTable组成,有几个HashTable并发级别concurrencyLevel就是几,默认16,这就叫分段锁,

ConcurrentSkipListMap/TreeMap

TreeMap与HashMap不同之处在于,TreeMap按照key的字典顺序来排序(升序),也可通过Comparator实现排序逻辑

TreeMap线程不安全,使用ConcurrentSkipListMap保证线程安全

ArrayList

非线程安全

CopyOnWriteArrayList

线程安全,通过ReentrantLock实现,add()方法加锁

但会对数组进行拷贝,在某段时间数据量翻倍造成内存溢出,不适用数据量过大的情况

读取新写入的数据会有延迟,因为数组的引用需要从旧数组转到新数组

 1     /**
 2      * Appends the specified element to the end of this list.
 3      *
 4      * @param e element to be appended to this list
 5      * @return {@code true} (as specified by {@link Collection#add})
 6      */
 7     public boolean add(E e) {
 8         final ReentrantLock lock = this.lock;
 9         lock.lock();
10         try {
11             Object[] elements = getArray();
12             int len = elements.length;
13             Object[] newElements = Arrays.copyOf(elements, len + 1);
14             newElements[len] = e;
15             setArray(newElements);
16             return true;
17         } finally {
18             lock.unlock();
19         }
20     }

HashSet

HashMap如果遇到相同的Key会对Value值进行覆盖,保证HashMap的Key是不重复的。

HashSet<T> 即利用HashMap这一特性保证Set集合中元素(KEY)不重复,其并不关心Value的值。

CopyOnWriteArraySet

 写的时候会判断数组中是否已经存在,若存在就不再写入,其他特性与CopyOnWriteArrayList相同

ConcurrentSkipListSet

 跳表实现原理同ConcurrentSkipListMap,KEY不重复且有序

队列QUEUE

ArrayBlockingQueue

线程安全,数组实现,ReentrantLock锁,只有一把锁,take() 和 put() 互斥,加的是同一把锁;

take()和put()方法是阻塞的,offer()和poll()方法是非阻塞的

构造函数制定队列长度capacity,利用参数count记录队列中元素数量,count=capacity时put操作阻塞(poll()方法直接返回null),count=0时take操作阻塞(offer()方法直接返回false),底层唤醒其实是利用condition的2个队列实现,参照1.3.1 Lock利用condition实现的阻塞队列;

put和take时利用takeIndex和putIndex记录下一个操作应该放到那个位置或从哪个位置取;

循环数组实现:当putIndex达到capacity时,takeIndex并不是+1,而是变成0,即下一个put操作会将元素放到队列头部;takeIndex原理相同

 1 public void put(E e) throws InterruptedException {
 2         checkNotNull(e);
 3         final ReentrantLock lock = this.lock;
 4         lock.lockInterruptibly();
 5         try {
 6             while (count == items.length)
 7                 notFull.await();
 8             enqueue(e);
 9         } finally {
10             lock.unlock();
11         }
12     }
13 
14     public E take() throws InterruptedException {
15         final ReentrantLock lock = this.lock;
16         lock.lockInterruptibly();
17         try {
18             while (count == 0)
19                 notEmpty.await();
20             return dequeue();
21         } finally {
22             lock.unlock();
23         }
24     }
25 
26     public boolean offer(E e) {
27         checkNotNull(e);
28         final ReentrantLock lock = this.lock;
29         lock.lock();
30         try {
31             if (count == items.length)
32                 return false;
33             else {
34                 enqueue(e);
35                 return true;
36             }
37         } finally {
38             lock.unlock();
39         }
40     }
41 
42     public E poll() {
43         final ReentrantLock lock = this.lock;
44         lock.lock();
45         try {
46             return (count == 0) ? null : dequeue();
47         } finally {
48             lock.unlock();
49         }
50     }
View Code

LinkedBlockingQueue

线程安全,链表实现,put和take各一把ReentrantLock锁,读和写操作加的是不同的锁,互不干扰,提高了读写性能

take()操作利用takeLock操作链表头部,put()利用putLock操作链表尾部,互不干扰,读和取不会互斥,并发度比ArrayBlockingQueue高一些

ConcurrentLinkedQueue

线程安全,并发度高,非阻塞队列,无锁,没有take() 和 put()这2个阻塞方法

利用CAS操作,采用自旋的方式将元素添加到链表尾部 或 将链表头部替换为null

SynchronousQueue

不常用

package com.study.list_set_queue.queue;

import java.util.concurrent.SynchronousQueue;

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

 */

public class Demo2_SyncQueueTest {
    static SynchronousQueue<String> 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

优先级队列,可以通过比较器对入队列的元素进行排序存储,进而改变出队列顺序

package com.study.list_set_queue.queue;

import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;

public class Demo4_PriorityBlockingQueue3 {
	public static void main(String args[]) {
		PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>(5, new Comparator<Student>() {
			@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;
	}
}
原文地址:https://www.cnblogs.com/yfzhou528/p/11297646.html