同步类容器
Vector、HashTable等古老的并发容器,都是使用Collections.synchronizedXXX等工厂方法创建的, 并发状态下只能有一个线程访问容器对象,性能很低(进行put或者get操作的时候之后一个线程能获取到锁)
例子
public class DemoThread26 {
public static void main(String[] args) throws Exception {
/*
* 使用以下方法被包裹的类将支持多线程
* Collections.synchronizedCollection(c);
* Collections.synchronizedList(list);
* Collections.synchronizedMap(m);
* Collections.synchronizedSet(s);
* Collections.synchronizedSortedMap(m);
* Collections.synchronizedSortedSet(s);
*/
// 非线程安全的List
final List<String> list = new ArrayList<String>();
// 线程安全的List
// final List<String> list = Collections.synchronizedList(new ArrayList<String>());
ExecutorService es = Executors.newFixedThreadPool(100);
//向list中并发加入1万个元素,如果是线程安全的那么list.size=1万,否则!=1万
for (int i = 0; i < 10000; i++) {
es.execute(new Runnable() {
@Override
public void run() {
list.add("5");
}
});
}
es.shutdown();
while (true) {
if (es.isTerminated()) {
System.out.println("所有的子线程都结束了!");
System.out.println(list.size());
if(list.size()!=10000){
System.out.println("线程不安全!");
}else{
System.out.println("线程安全!");
}
break;
}
}
}
}
直接使用ArrayList,结果如下
所有的子线程都结束了!
9977
线程不安全!
使用线程安全的list,结果如下
所有的子线程都结束了!
10000
线程安全!
synchronizedList源码
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
用的还是list,只是在调用方法之前需要去获得锁
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
Vector
查看add方法,直接在方法上加锁
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
HashTable
查看put方法,直接在方法上加锁
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
并发容器
-
JDK5.0之后提供了多种并发类容易可以替代同步类容器,提升性能、吞吐量
-
ConcurrentHashMap替代HashMap、HashTable
-
ConcurrentSkipListMap替代TreeMap (和HashMap的区别是它是有序的)
-
ConcurrentHashMap将hash表分为16个segment,每个segment单独进行锁控制,从而减小了锁的 粒度,提升了性能
ConcurrentHashMap
无论怎么改还是基于可见性和原子性
HashTable 实现是在整体加锁
ConcurrentHashMap将hash表分为16个segment,每个segment单独进行锁控制,从而减小了锁的 粒度,提升了性能
例子
通过并发下的运行时间对比ConcurrentHashMap与Hashtable的性能
class DemoDemo{
public DemoDemo() {
}
}
public class DemoThread27 {
//通过并发下的运行时间对比ConcurrentHashMap与Hashtable的性能
public static void testMap1(){
//final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String,Integer>();
final Hashtable<String, Integer> map = new Hashtable<String,Integer>();
for(int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
Long start = System.currentTimeMillis();
for(int i=0;i<1000000;i++){
map.put("a"+i, i);
}
System.out.println(System.currentTimeMillis()-start);
}
}).start();
}
}
//对比ConcurrentSkipListMap与SortedMap的性能
public static void testSkipListMap1() throws InterruptedException{
//高性能线程安全
//final ConcurrentSkipListMap<String, DemoDemo> skipMap = new ConcurrentSkipListMap<String,DemoDemo>();
//线程不安全、性能高
//final SortedMap<String, DemoDemo> skipMap = new TreeMap<String,DemoDemo>();
//低性能线程安全
final SortedMap<String, DemoDemo> skipMap = Collections.synchronizedSortedMap(new TreeMap<String,DemoDemo>());
final CountDownLatch countDownLatch = new CountDownLatch(10);
for(int i=0;i<10;i++){
new Thread(new Runnable() {
@Override
public void run() {
Long start = System.currentTimeMillis();
Random rn = new Random();
for(int i=0;i<1000;i++){
try {
skipMap.put("k"+i%10, new DemoDemo());
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("添加100个元素耗时:"+(System.currentTimeMillis()-start)+"毫秒");
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
//System.out.println(skipMap);
System.out.println(skipMap.size());
}
public static void testMap2(){
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String,Integer>();
map.put("a", 1);
map.put("b", 1);
map.put("c", 1);
map.put("d", 1);
//如果key已经存在则更新
map.put("a", 2);
System.out.println(map);
//如果key存在则不更新,不存在则添加
map.putIfAbsent("b", 2);
map.putIfAbsent("e", 3);
System.out.println(map);
}
public static void testSkipListMap2(){
ConcurrentSkipListMap<String, Integer> map = new ConcurrentSkipListMap<String, Integer>();
map.put("a", 1);
map.put("b1", 1);
map.put("c", 1);
map.put("d", 1);
//如果key已经存在则更新
map.put("a", 2);
System.out.println(map);
//如果key存在则不更新
map.putIfAbsent("b", 2);
System.out.println(map);
}
public static void main(String[] args) throws Exception {
testMap1();
// testMap2();
//testSkipListMap1();
// testSkipListMap2();
}
}
运行testMap1(),使用ConcurrentHashMap,结果如下
1116
1139
1041
1151
1153
1164
1173
1178
1180
1171
运行testMap1(),使用Hashtable,结果如下
1776
1781
1784
1793
1793
1794
1795
1796
1799
1799
同理testSkipListMap1
COW类容器
Copy On Write容器,简称COW;写时复制容器,向容器中添加元素时,先将容器进行Copy出一个新容器,然后将元素添加到新容器中,再将原容器的引用指向新容器。并发读的时候不需要锁定容器, 因为原容器没有变化,使用的是一种读写分离的思想。由于每次更新都会复制新容器,所以如果数据量较大,并且更新操作频繁则对内存消耗很高,建议在高并发读的场景下使用 (适合读多写少的情况)
CopyOnWriteArraySet基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是 CopyOnWriteArrayList的addIfAbsent方法, adIfAbsent方法同样采用锁保护,并创建一个新的大小+1的Object数组。遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。从以上分析可见,CopyOnWriteArraySet在add时每次都要进行数组的遍历,因此其性能会低于CopyOnWriteArrayList.
/**
* CopayOnWrite并发容器构建
* CopyOnWriteArrayList 替代 ArrayList、Vector (有序可重复的)
* CopyOnWriteArraySet 替代 LinkedHashSet (有序不重复)
* CopyOnWriteArrayList.add 与 CopyOnWriteArrayList.addIfAbsent 的区别
* CopyOnWriteArraySet是借助addIfAbsent方法实现的,由于需要去重,所以性能低于CopyOnWriteArrayList
*/
public class DemoThread28 {
private static volatile int count = 0;
public static void testCOWArrayList() throws Exception{
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list);
list.add(1); //无论元素是否已经存在都添加
System.out.println(list);
//如果元素不存在则不用添加,CopyOnWriteArraySet就是利用此函数实现的
//英文:Absent adj. 缺席的,不在场的; 缺少的,缺乏的; 不在意的,茫然的;
list.addIfAbsent(2);
System.out.println(list);
}
public static void testCOWArraySet() throws Exception{
final CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<Integer>();
set.add(2);
set.add(3);
set.add(1);
set.add(4);
System.out.println(set);
set.add(2);
set.add(3);
set.add(4);
System.out.println(set);
}
public static void main(String[] args) throws Exception {
// testCOWArrayList();
// testCOWArraySet();
}
}
运行testCOWArrayList,结果如下
[1, 2, 3, 4]
[1, 2, 3, 4, 1]
[1, 2, 3, 4, 1]
运行testCOWArraySet,结果如下
[2, 3, 1, 4]
[2, 3, 1, 4]
CopyOnWriteArrayList底层实现
/**
* Appends the element, if not present.
*
* @param e element to be added to this list, if absent
* @return {@code true} if the element was added
*/
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
/**
* A version of addIfAbsent using the strong hint that given
* recent snapshot does not contain e.
*/
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
//找到了就不添加
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
采用了读写分离
CopyOnWriteArraySet底层实现
基于CopyOnWriteArrayList,没有新东西
private final CopyOnWriteArrayList<E> al;
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element {@code e} to this set if
* the set contains no element {@code e2} such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.
*
* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return al.addIfAbsent(e);
}
COW容器的弱一致性
使用COW容器的iterator方法实际返回的是COWlterator实例,遍历的数据为快照数据,其他线程对于容器元素增加、删除、修改不对快照产生影响。
对java.util.concurrent.CopyOnWriteArrayList、java.util.concurrent.CopyOnWriteArraySet均适用。
CopyOnWriteArrayList
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
* {@code remove} method.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
继承ListIterator
tatic final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
//快照
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
//指针
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
//通过指针寻位返回对应数据
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
@SuppressWarnings("unchecked")
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor-1;
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code remove}
* is not supported by this iterator.
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code set}
* is not supported by this iterator.
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
* Not supported. Always throws UnsupportedOperationException.
* @throws UnsupportedOperationException always; {@code add}
* is not supported by this iterator.
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
Object[] elements = snapshot;
final int size = elements.length;
for (int i = cursor; i < size; i++) {
@SuppressWarnings("unchecked") E e = (E) elements[i];
action.accept(e);
}
cursor = size;
}
}
例子
public class SampleDemo {
public static void main(String[] args) {
// 用一个小例子来说明快照的产生
Object[] array = new Object[]{1,2,3};
Object[] snapshot = array;
Object[] newArray = new Object[]{1,2,3,4};
array = newArray;
System.out.println("array:"+Arrays.toString(array));
System.out.println("snapshot:"+Arrays.toString(snapshot));
}
}
模仿COW容器
array:[1, 2, 3, 4]
snapshot:[1, 2, 3]
COW容器证明读取的是快照,由于读写分离,读取的还是旧版本
public class COWDemo0 {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Iterator<Integer> iterator = list.iterator();
Thread td = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(4);
list.add(5);
}
});
td.start();
//线程一定会执行
td.join();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("------------------");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
执行结果
1
2
3
------------------
1
2
3
4
5
把Iterator<Integer> iterator = list.iterator();
放在td.join();之后,执行的结果如下,对迭代器获取后立刻使用,对时效性要求高则使用for循环
1
2
3
4
5
------------------
1
2
3
4
5
查看源码COWIterator,在构造函数中复制了快照
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
获取快照的时候调用
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
* {@code remove} method.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
而get是直接获取了,用的并不是快照
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}