【Java并发编程二】Java并发包

1.Java容器

1.1.同步容器

Vector

ArrayList是最经常使用的List实现类,内部是经过数组实现的,它容许对元素进行快速随机访问。数组的缺点是每一个元素之间不能有间隔,当数组大小不知足时须要增长存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,须要对数组进行复制、移动、代价比较高。所以,它适合随机查找和遍历,不适合插入和删除。java

Vector与ArrayList同样,也是经过数组实现的,不一样的是它支持线程的同步,即某一时刻只有一个线程可以写Vector,避免多线程同时写而引发的不一致性,但实现同步须要很高的花费,所以,访问它比访问ArrayList慢程序员

注意: Vector线程安全,数组的扩容默认2倍,能够自定义。而ArrayList数组的扩容是1.5倍数组

Vector源码类安全

Add方法源码类多线程

 

1     public synchronized boolean add(E e) {
2         modCount++;
3         ensureCapacityHelper(elementCount + 1);
4         elementData[elementCount++] = e;
5         return true;
6     }

 

Arraylist源码并发

Add方法源码高并发

1    public boolean add(E e) {
2         ensureCapacityInternal(size + 1);  // Increments modCount!!
3         elementData[size++] = e;
4         return true;
5     }

HashTable

 

HashMap是一个接口 是map接口的子接口,是将键映射到值的对象,其中键和值都是对象,而且不能包含重复键,但能够包含重复值。HashMap容许null key和null value,而hashtable不容许。性能

HashTable是线程安全的一个Collection。spa

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap容许空(null)键值(key),因为非线程安全,效率上可能高于Hashtable。线程

HashMap容许将null做为一个entry的key或者value,而Hashtable不容许。
HashMap把Hashtable的contains方法去掉了,改为containsvalue和containsKey。

注意: HashTable 是线程安全的,HashMap是线程不安全的

 

HashTable源码类

put方法源码类

 1  public synchronized V put(K key, V value) {
 2         // Make sure the value is not null
 3         if (value == null) {
 4             throw new NullPointerException();
 5         }
 6 
 7         // Makes sure the key is not already in the hashtable.
 8         Entry<?,?> tab[] = table;
 9         int hash = key.hashCode();
10         int index = (hash & 0x7FFFFFFF) % tab.length;
11         @SuppressWarnings("unchecked")
12         Entry<K,V> entry = (Entry<K,V>)tab[index];
13         for(; entry != null ; entry = entry.next) {
14             if ((entry.hash == hash) && entry.key.equals(key)) {
15                 V old = entry.value;
16                 entry.value = value;
17                 return old;
18             }
19         }
20 
21         addEntry(hash, key, value, index);
22         return null;
23     }

 

HashMap源码类

put方法源码类

1     public V put(K key, V value) {
2 
3         return putVal(hash(key), key, value, false, true);
4 
5     }

 

Collections.synchronized*()

Collections类下的synchronized* 方法能够将不安全的集合变成线程安全的集合


例如:
1 Collections.synchronizedMap(new HashMap<>(10));
2 Collections.synchronizedList(new ArrayList<>());
3 Collections.synchronizedSet(new HashSet<>());
 

1.2.并发容器

ConcurrentHashMap

ConcurrentHashMap与HashMap同样是一个哈希表,可是它使用彻底不一样的锁策略,能够提供更好的并发性和可伸缩性。在ConcurrentHashMap之前,程序使用一个公共锁同步一个方法,并严格地控制只能在一个线程中能够同时访问容器,而ConcurrentHashMap使用一个更为细化的锁机制,名叫分离锁。这个机制容许任意数量的读线程能够并发访问Map,读者和写者也能够并发访问Map,而且有限数量的写进程还能够并发修改Map,结果是为并发访问带来更高的吞吐量,同时几乎没有损失单个线程访问的性能。

  ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHshMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap相似,是一种数组和链表结构,一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每一个Segment守护着HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先得到它对应的Segment的锁。

 

注意:ConcurrentHashMap是线程安全的,因为代码中大多数全局变量使用volatile关键字声明,因此性能较好

 

ConcurrentSkipListMap

ConcurrentSkipListMap是在ConcurrentHashMap的基础上,增长了排序功能

 

2.Java并发队列

2.1.ConcurrentLinkedQueue

ConcurrentLinkedQueue:是一个适用于高并发场景下的队列,经过无所的方式,实现了高并发状态下的高性能,一般ConcurrentLinkedQueue性能好于BlockingQueue。他是一个基于链接节点的无界线程安全队列。该队列的线程遵循FIFO的原则,不容许null元素。

add 和offer() 都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别)
poll() 和peek() 都是取头元素节点,区别在于前者会删除元素,后者不会。

 

2.2.BlockingQueue

阻塞队列(BlockingQueue)是一个支持两个附加操做的队列。这两个附加的操做是:

在队列为空时,获取元素的线程会等待队列变为非空。

当队列满时,写入(存储)元素的线程会等待队列可用。

在Java中,BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本开始提供),由上面介绍的阻塞队列的特性可知,阻塞队列是线程安全的。

在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。经过这些高效而且线程安全的队列类,为咱们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的全部成员,包括他们各自的功能以及常见使用场景。

经常使用的队列主要有如下两种:(固然经过不一样的实现方式,还能够延伸出不少不一样类型的队列,DelayQueue就是其中的一种)

  先进先出(FIFO):先插入的队列的元素也最早出队列,相似于排队的功能。从某种程度上来讲这种队列也体现了一种公平性。

  后进先出(LIFO):后插入队列的元素最早出队列,这种队列优先处理最近发生的事件。

      多线程环境中,经过队列能够很容易实现数据共享,好比经典的“生产者”和“消费者”模型中,经过队列能够很便利地实现二者之间的数据共享。假设咱们有若干生产者线程,另外又有若干个消费者线程。若是生产者线程须要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就能够很方便地解决他们之间的数据共享问题。但若是生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的状况呢?理想状况下,若是生产者产出数据的速度大于消费者消费的速度,而且当生产出来的数据累积到必定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在concurrent包发布之前,在多线程环境下,咱们每一个程序员都必须去本身控制这些细节,尤为还要兼顾效率和线程安全,而这会给咱们的程序带来不小的复杂度。好在此时,强大的concurrent包横空出世了,而他也给咱们带来了强大的BlockingQueue。(在多线程领域:所谓阻塞,在某些状况下会挂起线程(即阻塞),一旦条件知足,被挂起的线程又会自动被唤醒)

 

 

ArrayBlockingQueue

ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,咱们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。

ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。下面是一个初始化和使用ArrayBlockingQueue的例子:

 

LinkedBlockingQueue

LinkedBlockingQueue阻塞队列大小的配置是可选的,若是咱们初始化时指定一个大小,它就是有边界的,若是不指定,它就是无边界的。说是无边界,实际上是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。

和ArrayBlockingQueue同样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。

 

PriorityBlockingQueue

PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue同样。须要注意,PriorityBlockingQueue中容许插入null对象。

全部插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就是按照咱们对这个接口的实现来定义的。

另外,咱们能够从PriorityBlockingQueue得到一个迭代器Iterator,但这个迭代器并不保证按照优先级顺序进行迭代。

SynchronousQueue

SynchronousQueue队列内部仅容许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另外一个线程消费。

相关文章
相关标签/搜索