Java并发集合
- 阻塞队列(BlockingQueue)
- 核心方法
- 阻塞队列类型
- 类结构
- ArrayBlockingQueue
- DelayBlockingQueue
- TransferQueue
- LinkedTransferQueue
- PriorityBlockingQueue
- SynchronousQueue
- 并发字典(ConcurrentHashMap)
- 核心方法
- ConcurrentHashMap
- 并发字典(ConcurrentSkipListMap)
- 并发集合(ConcurrentSkipListSet)
- 写时复制集合
- 类结构
- CopyOnWriteArrayList
- CopyOnWriteArraySet
阻塞队列(BlockingQueue)
在Java的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。
多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。然而,在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。好在此时,强大的concurrent包横空出世了,而他也给我们带来了强大的BlockingQueue。(在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒)
作为BlockingQueue的使用者,我们再也不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了。
核心方法
入列:
方法签名 | 是否加锁 | 是否阻塞 | 阻塞是否可中断 | 其它 |
public boolean add(E e) | 否 | 否 | 不涉及 | 继承自AbstractQueue |
public boolean offer(E e) | 是 | 否 | 否 | 插入成功后唤醒等待线程 |
public void put(E e) throws InterruptedException | 是 | 有界队列阻塞,无界队列不阻塞 | 是 | 插入成功后唤醒等待线程 |
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException | 是 | 有界队列阻塞,无界队列不阻塞 | 是 | 插入成功后唤醒等待线程 |
出列:
方法签名 | 是否加锁 | 是否阻塞 | 阻塞是否可中断 | 其它 |
public E poll() | 是 | 否 | 不涉及 | 元素出列成功后唤醒等待线程 |
public E take() throws InterruptedException | 是 | 是 | 是 | 元素出列成功后唤醒等待线程 |
public E poll(long timeout, TimeUnit unit) throws InterruptedException | 是 | 是 | 是 | 元素出列成功后唤醒等待线程 |
public E peek() | 是 | 否 | 不涉及 | 只读取,不删除元素 |
public int drainTo(Collection<? super E> c) | 是 | 否 | 不涉及 | 一次出列全部元素 |
int drainTo(Collection<? super E> c, int maxElements) | 是 | 否 | 不涉及 | 一次出列指定个数的元素 |
阻塞队列类型
类名 | 介绍 | 是否有界 | 是否加锁 | 底层数据结构 |
ArrayBlockingQueue | 是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。支持公平锁和非公平锁。 | 是 | 是 | ArrayList |
LinkedBlockingQueue | 一个由链表结构组成的有界队列,此队列的长度为Integer.MAX_VALUE。此队列按照先进先出的顺序进行排序。 | 构造函数指定有界或无界 | 是 | LinkedList |
PriorityBlockingQueue | 一个支持线程优先级排序的无界队列,默认自然序进行排序,也可以自定义实现compareTo()方法来指定元素排序规则,不能保证同优先级元素的顺序。 | 否 | 是 | 堆 |
DelayQueue | 一个实现PriorityBlockingQueue实现延迟获取的无界队列,在创建元素时,可以指定多久才能从队列中获取当前元素。只有延时期满后才能从队列中获取元素。(DelayQueue可以运用在以下应用场景:1.缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。2.定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。) | 否 | 是 | 堆 |
SynchronousQueue | 一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。支持公平锁和非公平锁。SynchronousQueue的一个使用场景是在线程池里。Executors.newCachedThreadPool()就使用了SynchronousQueue,这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程空闲了60秒后会被回收。 | 是 | 是 | 无 |
LinkedTransferQueue | 一个由链表结构组成的无界阻塞队列,相当于其它队列,LinkedTransferQueue队列多了transfer和tryTransfer方法。 | 否 | 是 | LinkedList |
LinkedBlockingDeque | 一个由链表结构组成的双向阻塞队列。队列头部和尾部都可以添加和移除元素,多线程并发时,可以将锁的竞争最多降到一半。 | 否 | 是 | LinkedList |
类结构
«interface»
BlockingQueue
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+take() : E
+poll(long timeout, TimeUnit unit) : E
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
ArrayBlockingQueue
lock : ReentrantLock
notEmpty : Condition
notFull : Condition
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
ReentrantLock
«interface»
Condition
LinkedBlockingQueue
lock : ReentrantLock
notEmpty : Condition
notFull : Condition
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
PriorityBlockingQueue
lock : ReentrantLock
notEmpty : Condition
notFull : Condition
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
SynchronousQueue
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
Queue
AbstractQueue
DelayQueue
lock : ReentrantLock
notEmpty : Condition
notFull : Condition
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
«interface»
Delayed
long getDelay
«interface»
BlockingDeque
+addFirst(E e)
+addLast(E e)
+offerFirst(E e) : boolean
+offerLast(E e) : boolean
+putFirst(E e)
+putLast(E e)
+offerFirst(E e, long timeout, TimeUnit unit) : boolean
+offerLast(E e, long timeout, TimeUnit unit) : boolean
+takeFirst() : E
+takeLast() : E
+pollFirst(long timeout, TimeUnit unit) : E
+pollLast(long timeout, TimeUnit unit) : E
+removeFirstOccurrence(Object o) : boolean
+removeLastOccurrence(Object o) : boolean
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+remove() : E
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+element() : E
+peek() : E
+remove(Object o) : boolean
+contains(Object o) : boolean
+size() : int
+iterator() : Iterator<E>
+push(E e)
LinkedBlockingDeque
lock : ReentrantLock
notEmpty : Condition
notFull : Condition
+addFirst(E e)
+addLast(E e)
+offerFirst(E e) : boolean
+offerLast(E e) : boolean
+putFirst(E e)
+putLast(E e)
+offerFirst(E e, long timeout, TimeUnit unit) : boolean
+offerLast(E e, long timeout, TimeUnit unit) : boolean
+removeFirst() : E
+removeLast() : E
+pollFirst() : E
+pollLast() : E
+takeFirst() : E
+takeLast() : E
+pollFirst(long timeout, TimeUnit unit) : E
+pollLast(long timeout, TimeUnit unit) : E
+getFirst() : E
+getLast() : E
+peekFirst() : E
+peekLast() : E
+removeFirstOccurrence(Object o) : boolean
+removeLastOccurrence(Object o) : boolean
+add(E e) : boolean
+offer(E e) : boolean
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+remove() : E
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+element() : E
+peek() : E
+remainingCapacity() : int
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+push(E e)
+pop() : E
+remove(Object o) : boolean
+size() : int
+contains(Object o) : boolean
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+iterator() : Iterator<E>
+descendingIterator() : Iterator<E>
«interface»
TransferQueue
+tryTransfer(E e) : boolean
+transfer(E e)
+tryTransfer(E e, long timeout, TimeUnit unit) : boolean
+hasWaitingConsumer() : boolean
+getWaitingConsumerCount() : int
LinkedTransferQueue
+add(E e) : boolean
+offer(E e) : boolean
+put(E e)
+offer(E e, long timeout, TimeUnit unit) : boolean
+poll() : E
+take() : E
+poll(long timeout, TimeUnit unit) : E
+peek() : E
+size() : int
+remainingCapacity() : int
+remove(Object o) : boolean
+contains(Object o) : boolean
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+toArray() : Object[]
+toArray(T[] a) : T[]
+toString() : String
+clear()
+drainTo(Collection<? super E> c) : int
+drainTo(Collection<? super E> c, int maxElements) : int
+iterator() : Iterator<E>
+tryTransfer(E e) : boolean
+transfer(E e)
+tryTransfer(E e, long timeout, TimeUnit unit) : boolean
+hasWaitingConsumer() : boolean
+getWaitingConsumerCount() : int
Queue
BlockingQueue
AbstractQueue
Comparable
PriorityQueue
ArrayBlockingQueue
package com.qupeng.concurrent.blockingqueue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestArrayBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(2);
Producer producer1 = new Producer(blockingQueue);
Producer producer2 = new Producer(blockingQueue);
Producer producer3 = new Producer(blockingQueue);
Producer producer4 = new Producer(blockingQueue);
Producer producer5 = new Producer(blockingQueue);
Consumer consumer = new Consumer(blockingQueue);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
executorService.execute(consumer);
Thread.sleep(10 * 1000);
producer1.isRunning = false;
producer2.isRunning = false;
producer3.isRunning = false;
producer4.isRunning = false;
producer5.isRunning = false;
consumer.isRunning = false;
executorService.shutdownNow();
}
static class Consumer implements Runnable {
private boolean isRunning = true;
private BlockingQueue<String> blockingQueue;
public Consumer(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while(isRunning) {
String product = blockingQueue.take();
System.out.println(String.format("%60sConsume : %s", " ", product));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
}
}
}
static class Producer implements Runnable {
private boolean isRunning = true;
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<String> blockingQueue;
public Producer(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while (isRunning) {
String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
if (blockingQueue.size() == 2) {
System.out.println("Blocking queue is full. Producer " + Thread.currentThread().getId() + " will be blocked");
}
blockingQueue.put(product);
System.out.println("Producer " + Thread.currentThread().getId() + " was awakened, produced : " + product);
}
} catch (InterruptedException e) {
}
}
}
}
DelayBlockingQueue
package com.qupeng.concurrent.blockingqueue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestDelayBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Product> blockingQueue = new DelayQueue<>();
Producer producer1 = new Producer(blockingQueue, 5000);
Producer producer2 = new Producer(blockingQueue, 4000);
Producer producer3 = new Producer(blockingQueue, 3000);
Producer producer4 = new Producer(blockingQueue, 2000);
Producer producer5 = new Producer(blockingQueue, 1000);
Consumer consumer = new Consumer(blockingQueue);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
executorService.execute(consumer);
Thread.sleep(10 * 1000);
producer1.isRunning = false;
producer2.isRunning = false;
producer3.isRunning = false;
producer4.isRunning = false;
producer5.isRunning = false;
consumer.isRunning = false;
executorService.shutdownNow();
}
static class Product implements Delayed {
private String name;
private long start = System.currentTimeMillis();
private long delay;
public Product(String name, long delay) {
this.name = name;
this.delay = delay;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((start + delay) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
long diff = this.delay - ((Product)o).delay;
if (diff > 0) {
return 1;
} else if (diff == 0) {
return 0;
} else {
return -1;
}
}
}
static class Consumer implements Runnable {
private boolean isRunning = true;
private BlockingQueue<Product> blockingQueue;
public Consumer(BlockingQueue<Product> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while(isRunning) {
Product product = blockingQueue.take();
System.out.println(String.format("%60sConsume : %s delayed; ", " ", product.name, product.delay));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
}
}
}
static class Producer implements Runnable {
private boolean isRunning = true;
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<Product> blockingQueue;
private long delay;
public Producer(BlockingQueue<Product> blockQueue, long delay) {
this.blockingQueue = blockQueue;
this.delay = delay;
}
@Override
public void run() {
try {
while (isRunning) {
Product product = new Product(Thread.currentThread().getId() + "-" + index.getAndAdd(1), delay);
blockingQueue.put(product);
System.out.println("Producer " + Thread.currentThread().getId() + " produced : " + product.name);
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
}
}
}
TransferQueue
TransferQueue接口定义了非阻塞方式的入队和出队方法
方法签名 | 说明 |
boolean tryTransfer(E e) | 如果有消费线程阻塞在队列上,那么将数据传送给消费线程并返回ture;否则返回false |
void transfer(E e) throws InterruptedException | 如果有消费线程阻塞在队列上,那么将数据传送给消费线程;否则阻塞直到有消费线程成功接收数据 |
boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException | 如果有消费线程阻塞在队列上,那么将数据传送给消费线程并返回ture;否则阻塞指定的时间,然后返回false |
boolean hasWaitingConsumer() | 是否有阻塞的消费线程 |
int getWaitingConsumerCount() | 获取阻塞的消费线程的数目 |
LinkedTransferQueue
package com.qupeng.concurrent.blockingqueue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestLinkedTransferQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new LinkedTransferQueue();
ProducerNotBlocked producer = new ProducerNotBlocked(blockingQueue);
ProducerBlocked producer1 = new ProducerBlocked(blockingQueue);
Consumer consumer = new Consumer(blockingQueue);
Consumer consumer1 = new Consumer(blockingQueue);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(producer);
executorService.execute(producer1);
executorService.execute(consumer);
executorService.execute(consumer1);
Thread.sleep(10 * 1000);
producer.isRunning = false;
producer1.isRunning = false;
consumer.isRunning = false;
consumer1.isRunning = false;
executorService.shutdownNow();
}
static class Consumer implements Runnable {
private boolean isRunning = true;
private BlockingQueue<String> blockingQueue;
public Consumer(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while(isRunning) {
String product = blockingQueue.take();
System.out.println(String.format("%60sConsume : %s", " ", product));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
}
}
}
static class ProducerNotBlocked implements Runnable {
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<String> blockingQueue;
private boolean isRunning = true;
public ProducerNotBlocked(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
TransferQueue transferQueue = (TransferQueue)blockingQueue;
while (isRunning) {
System.out.println(String.format("There are %d waiting consumers", transferQueue.getWaitingConsumerCount()));
String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
System.out.println("Try to transfer product to queue...");
if (transferQueue.hasWaitingConsumer() && transferQueue.tryTransfer(product)) {
System.out.println(String.format("Producer %s produced : %s", Thread.currentThread().getId(), product));
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
static class ProducerBlocked implements Runnable {
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<String> blockingQueue;
private boolean isRunning = true;
public ProducerBlocked(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
TransferQueue transferQueue = (TransferQueue)blockingQueue;
while (isRunning) {
try {
System.out.println(String.format("There are %d waiting consumers", transferQueue.getWaitingConsumerCount()));
String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
transferQueue.transfer(product);
System.out.println(String.format("Producer %s produced : %s", Thread.currentThread().getId(), product));
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}
}
PriorityBlockingQueue
package com.qupeng.concurrent.blockingqueue;
import java.util.Comparator;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestPriorityBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Product> blockingQueue = new PriorityBlockingQueue<>(2, new Comparator<Product>() {
@Override
public int compare(Product o1, Product o2) {
return Integer.valueOf(o2.name.split("-")[0]).compareTo(Integer.valueOf(o1.name.split("-")[0]));
}
});
Producer producer1 = new Producer(blockingQueue);
Producer producer2 = new Producer(blockingQueue);
Producer producer3 = new Producer(blockingQueue);
Producer producer4 = new Producer(blockingQueue);
Producer producer5 = new Producer(blockingQueue);
Consumer consumer = new Consumer(blockingQueue);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
executorService.execute(consumer);
Thread.sleep(10 * 1000);
producer1.isRunning = false;
producer2.isRunning = false;
producer3.isRunning = false;
producer4.isRunning = false;
producer5.isRunning = false;
consumer.isRunning = false;
executorService.shutdownNow();
}
static class Product implements Comparable {
private String name;
public Product(String name) {
this.name = name;
}
@Override
public int compareTo(Object o) {
Product prod1 = (Product)o;
return Integer.valueOf(this.name.split("-")[0]).compareTo(Integer.valueOf(prod1.name.split("-")[0]));
}
}
static class Consumer implements Runnable {
private boolean isRunning = true;
private BlockingQueue<Product> blockingQueue;
public Consumer(BlockingQueue<Product> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while(isRunning) {
Product product = blockingQueue.take();
System.out.println(String.format("%60sConsume : %s", " ", product.name));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
}
}
}
static class Producer implements Runnable {
private boolean isRunning = true;
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<Product> blockingQueue;
public Producer(BlockingQueue<Product> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while (isRunning) {
Product product = new Product(Thread.currentThread().getId() + "-" + index.getAndAdd(1));
blockingQueue.put(product);
System.out.println("produce : " + product.name);
Thread.sleep(500);
}
} catch (InterruptedException e) {
}
}
}
}
SynchronousQueue
package com.qupeng.concurrent.blockingqueue;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestSynchronousQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new SynchronousQueue();
Producer producer1 = new Producer(blockingQueue);
Producer producer2 = new Producer(blockingQueue);
Producer producer3 = new Producer(blockingQueue);
Producer producer4 = new Producer(blockingQueue);
Producer producer5 = new Producer(blockingQueue);
Consumer consumer = new Consumer(blockingQueue);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
executorService.execute(consumer);
Thread.sleep(10 * 1000);
producer1.isRunning = false;
producer2.isRunning = false;
producer3.isRunning = false;
producer4.isRunning = false;
producer5.isRunning = false;
consumer.isRunning = false;
executorService.shutdownNow();
}
static class Consumer implements Runnable {
private boolean isRunning = true;
private BlockingQueue<String> blockingQueue;
public Consumer(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
Thread.sleep(3000);
while(isRunning) {
String product = blockingQueue.take();
System.out.println(String.format("%60sConsume : %s", " ", product));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
}
}
}
static class Producer implements Runnable {
private boolean isRunning = true;
private AtomicInteger index = new AtomicInteger(1);
private BlockingQueue<String> blockingQueue;
public Producer(BlockingQueue<String> blockQueue) {
this.blockingQueue = blockQueue;
}
@Override
public void run() {
try {
while (isRunning) {
String product = "product-" + Thread.currentThread().getId() + "-" + index.getAndAdd(1);
System.out.println("Producer " + Thread.currentThread().getId() + " try to put : " + product);
blockingQueue.put(product);
System.out.println("Producer " + Thread.currentThread().getId() + " was awakened, produced : " + product);
}
} catch (InterruptedException e) {
}
}
}
}
并发字典(ConcurrentHashMap)
核心方法
并发Map的统一接口ConcurrentMap中新增了4个方法,以CAS的方式来提升并发的效率。
方法签名 | 说明 |
V putIfAbsent(K key, V value) | 如果key对应的值value不存在就put,且返回null。如果key对应的值value已存在,则返回已存在的值,且value不能为null,否则会报空指针异常。 |
boolean remove(Object key, Object value) | 仅当key存在并且value等于实参时,删除并返回true;否则返回false。 |
boolean replace(K key, V oldValue, V newValue) | 仅当key存在并且value等于实参时,覆盖value并返回true;否则返回false。 |
V replace(K key, V value) | 如果key对应的value存在就put,且返回原有值;否则直接返回null。 |
HashMap 在高并发下会出现链表环,从而导致程序出现死循环。高并发下避免 HashMap 出问题的方法有两种,一是使用 HashTable,二是使用 Collections.syncronizedMap。但是这两种方法的性能都能差。因为这两个在执行读写操作时都是将整个集合加锁,导致多个线程无法同时读写集合。高并发下的 HashMap 出现的问题就需要 ConcurrentHashMap 来解决了。
ConcurrentHashMap 中有一个 Segment 的概念。Segment 本身就相当于一个 HashMap 对象。同 HashMap 一样,Segment 包含一个 HashEntry 数组,数组中的每一个 HashEntry 既是一个键值对,也是一个链表的头节点。
ConcurrentHashMap 集合中有 2 的N次方个 Segment 对象,共同保存在一个名为 segments 的数组当中。
锁分段技术:
首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据仍能被其他线程访问。
ConcurrentHashMap 提供了与 Hashtable 和 SynchronizedMap 不同的锁机制。Hashtable 中采用的锁机制是一次锁住整个 hash 表,从而在同一时刻只能由一个线程对其进行操作;而 ConcurrentHashMap 中则是一次锁住一个桶。
ConcurrentHashMap 默认将 hash 表分为16个桶,诸如 get、put、remove 等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
«interface»
ConcurrentMap
+putIfAbsent(K key, V value) : V
+remove(Object key, Object value) : boolean
+replace(K key, V oldValue, V newValue) : boolean
+replace(K key, V value) : V
ConcurrentHashMap
DEFAULT_INITIAL_CAPACITY=16 : int
DEFAULT_LOAD_FACTOR = 0.75f : float
DEFAULT_CONCURRENCY_LEVEL = 16 : int
MAXIMUM_CAPACITY = 1 << 30 : int
MIN_SEGMENT_TABLE_CAPACITY = 2 : int
MAX_SEGMENTS = 1 << 16 : int
RETRIES_BEFORE_LOCK = 2 : int
segmentMask : int
segmentShift : int
segments : Segment<K,V>[]
keySet : Set<K>
entrySet: Set<Entry>
values : Collection<V>
~entryAt(HashEntry<K,V>[] tab, int i) : HashEntry<KV>
+size() : int
+isEmpty() : boolean
+containsValue(Object value) : boolean
+containsKey(Object key) : boolean
+get(Object key) : V
+contains(Object value) : boolean
+put(K key, V value) : V
+putIfAbsent(K key, V value) : V
+remove(Object key) : V
+putAll(Map<? extends K, ? extends V> m)
+remove(Object key, Object value) : boolean
+replace(K key, V oldValue, V newValue) : boolean
+replace(K key, V value) : V
+clear()
+keySet() : Set<K>
+values() : Collection<V>
+entrySet() : Set<Entry>
+keys() : Enumeration<K>
+elements() : Enumeration<V>
-rehash(HashEntry<K,V> node)
HashEntry
~setNext(HashEntry<K,V> n)
Segment
table : HashEntry<K,V>[]
«abstract»
HashIterator
+hasNext() : boolean
~nextEntry() : Entry<KV>
+remove()
Map
Serializable
AbstractMap
ReentrantLock
ConcurrentHashMap
如果5个线程同时更新HashMap中的同一个key:counter对应的value,每一个线程都给这个值加10,最终的counter值应该为50. 如何实现?
普通HashMap实现,最终的值肯定远小于50:
package com.qupeng.concurrent.map;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestHashMap {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> map = new HashMap(8, (float) 0.75);
ExecutorService executorService = Executors.newCachedThreadPool();
Producer producer1 = new Producer(map);
Producer producer2 = new Producer(map);
Producer producer3 = new Producer(map);
Producer producer4 = new Producer(map);
Producer producer5 = new Producer(map);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
while (!producer1.isStop || !producer2.isStop || !producer3.isStop || !producer4.isStop || !producer5.isStop) {
Thread.sleep(1000);
}
System.out.println("The total number in the map : " + map.get("counter"));
executorService.shutdownNow();
}
static class Producer implements Runnable {
private Map<String, Integer> map;
private volatile boolean isStop = false;
public Producer(Map concurrentMap) {
this.map = concurrentMap;
}
@Override
public void run() {
int i = 10;
while (i-- > 0) {
Integer oldValue = map.get("counter");
if (null == oldValue) {
map.put("counter", 1);
} else {
map.put("counter", oldValue + 1);
}
}
isStop = true;
}
}
}
synchronized+CountDownLatch实现,执行时间31毫秒:
package com.qupeng.concurrent.map;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestHashMapV2 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
final CountDownLatch latch = new CountDownLatch(5);
Map<String, Integer> map = new HashMap(8, (float) 0.75);
ExecutorService executorService = Executors.newCachedThreadPool();
Producer producer1 = new Producer(map, latch);
Producer producer2 = new Producer(map, latch);
Producer producer3 = new Producer(map, latch);
Producer producer4 = new Producer(map, latch);
Producer producer5 = new Producer(map, latch);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
latch.await();
System.out.println("The total number in the map : " + map.get("counter"));
System.out.println("Execution time : " + (System.currentTimeMillis() - start));
executorService.shutdownNow();
}
static class Producer implements Runnable {
private Map<String, Integer> map;
private CountDownLatch latch;
public Producer(Map concurrentMap, CountDownLatch latch) {
this.map = concurrentMap;
this.latch = latch;
}
@Override
public void run() {
int i = 10;
while (i-- > 0) {
synchronized(this.map) {
Integer oldValue = map.get("counter");
if (null == oldValue) {
map.put("counter", 1);
} else {
map.put("counter", oldValue + 1);
}
}
}
latch.countDown();
}
}
}
ConcurrentHashMap实现:
package com.qupeng.concurrent.map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestConcurrentHashMap {
public static void main(String[] args) throws InterruptedException {
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap(8, (float) 0.75);
ExecutorService executorService = Executors.newCachedThreadPool();
Producer producer1 = new Producer(concurrentMap);
Producer producer2 = new Producer(concurrentMap);
Producer producer3 = new Producer(concurrentMap);
Producer producer4 = new Producer(concurrentMap);
Producer producer5 = new Producer(concurrentMap);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
while (!producer1.isStop || !producer2.isStop || !producer3.isStop || !producer4.isStop || !producer5.isStop) {
Thread.sleep(1000);
}
System.out.println("The total number in the map : " + concurrentMap.get("counter"));
executorService.shutdownNow();
}
static class Producer implements Runnable {
private ConcurrentMap<String, Integer> concurrentMap;
private volatile boolean isStop = false;
public Producer(ConcurrentMap concurrentMap) {
this.concurrentMap = concurrentMap;
}
@Override
public void run() {
int i = 10;
while (i-- > 0) {
while (true) {
Integer oldValue = concurrentMap.get("counter");
if (null == oldValue) {
if (null == concurrentMap.putIfAbsent("counter", 1)) {
break;
}
} else {
if (concurrentMap.replace("counter", oldValue, oldValue + 1)) {
break;
}
}
}
}
isStop = true;
}
}
}
ConcurrentHashMap+AtomicInteger+CountDownLatch,执行时间16毫秒
package com.qupeng.concurrent.map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class TestConcurrentHashMapV2 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
final CountDownLatch latch = new CountDownLatch(5);
ConcurrentMap<String, AtomicInteger> concurrentMap = new ConcurrentHashMap(8, (float) 0.75);
ExecutorService executorService = Executors.newCachedThreadPool();
Producer producer1 = new Producer(concurrentMap, latch);
Producer producer2 = new Producer(concurrentMap, latch);
Producer producer3 = new Producer(concurrentMap, latch);
Producer producer4 = new Producer(concurrentMap, latch);
Producer producer5 = new Producer(concurrentMap, latch);
executorService.execute(producer1);
executorService.execute(producer2);
executorService.execute(producer3);
executorService.execute(producer4);
executorService.execute(producer5);
latch.await();
System.out.println("The total number in the map : " + concurrentMap.get("counter").intValue());
System.out.println("Execution time : " + (System.currentTimeMillis() - start));
executorService.shutdownNow();
}
static class Producer implements Runnable {
private ConcurrentMap<String, AtomicInteger> concurrentMap;
private CountDownLatch cutDownLatch;
public Producer(ConcurrentMap concurrentMap, CountDownLatch latch) {
this.concurrentMap = concurrentMap;
this.cutDownLatch = latch;
}
@Override
public void run() {
int i = 10;
while (i-- > 0) {
AtomicInteger oldValue = concurrentMap.get("counter");
if (null == oldValue) {
AtomicInteger newValue = new AtomicInteger(1);
oldValue = concurrentMap.putIfAbsent("counter", newValue);
if (null != oldValue) {
i++;
}
} else {
oldValue.incrementAndGet();
}
}
cutDownLatch.countDown();
}
}
}
并发字典(ConcurrentSkipListMap)
ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表,使用场景对标TreeMap。内部是SkipList(跳表)结构实现,在理论上能够O(log(n))时间内完成查找、插入、删除操作。跳表
是一种随机化的数据结构,其数据元素按照key(键)排序,所以跳表是有序的集合,跳表为了提高查找、插入和删除操作的性能,在原有的有序链表之上随机的让一部分数据元素分布到更多层链表中,从而在查找、插入和删除操作时可以跳过一些不可能涉及的数据元素节点,从而提高效率。
目前常用的key-value数据结构有三种:Hash表、红黑树、SkipList,它们各自有着不同的优缺点(不考虑删除操作):
- Hash表:插入、查找最快,为O(1);如使用链表实现则可实现无锁;数据有序化需要显式的排序操作。
- 红黑树:插入、查找为O(logn),但常数项较小;无锁实现的复杂性很高,一般需要加锁;数据天然有序。
- SkipList:插入、查找为O(logn),但常数项比红黑树要大;底层结构为链表,可无锁实现;数据天然有序。
如果要实现一个key-value结构,需求的功能有插入、查找、迭代、修改,那么首先Hash表就不是很适合了,因为迭代的时间复杂度比较高;而红黑树的插入很可能会涉及多个结点的旋转、变色操作,因此需要在外层加锁,这无形中降低了它可能的并发度。而SkipList底层是用链表实现的,可以实现为lock free,同时它还有着不错的性能(单线程下只比红黑树略慢),非常适合用来实现我们需求的那种key-value结构。
LevelDB、Reddis的底层存储结构就是用的SkipList。
«interface»
ConcurrentMap
+putIfAbsent(K key, V value) : V
+remove(Object key, Object value) : boolean
+replace(K key, V oldValue, V newValue) : boolean
+replace(K key, V value) : V
«interface»
ConcurrentNavigableMap
+subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) : ConcurrentNavigableMap<KV>
+headMap(K toKey, boolean inclusive) : ConcurrentNavigableMap<KV>
+tailMap(K fromKey, boolean inclusive) : ConcurrentNavigableMap<KV>
+subMap(K fromKey, K toKey) : ConcurrentNavigableMap<KV>
+headMap(K toKey) : ConcurrentNavigableMap<KV>
+tailMap(K fromKey) : ConcurrentNavigableMap<KV>
+descendingMap() : ConcurrentNavigableMap<KV>
+navigableKeySet() : NavigableSet<K>
+keySet() : NavigableSet<K>
+descendingKeySet() : NavigableSet<K>
ConcurrentSkipListMap
-findPredecessor(Comparable<? super K> key) : Node<KV>
-findNode(Comparable<? super K> key) : Node<KV>
-doGet(Object okey) : V
-doPut(K kkey, V value, boolean onlyIfAbsent) : V
-randomLevel() : int
-insertIndex(Node<K,V> z, int level)
-addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel)
-doRemove(Object okey, Object value) : V
-tryReduceLevel()
+clone() : ConcurrentSkipListMap<KV>
+containsKey(Object key) : boolean
+get(Object key) : V
+put(K key, V value) : V
+remove(Object key) : V
+containsValue(Object value) : boolean
+size() : int
+isEmpty() : boolean
+clear()
+keySet() : NavigableSet<K>
+navigableKeySet() : NavigableSet<K>
+values() : Collection<V>
+entrySet() : Set<Entry>
+descendingMap() : ConcurrentNavigableMap<KV>
+descendingKeySet() : NavigableSet<K>
+equals(Object o) : boolean
+putIfAbsent(K key, V value) : V
+remove(Object key, Object value) : boolean
+replace(K key, V oldValue, V newValue) : boolean
+replace(K key, V oldValue, V newValue) : boolean
+replace(K key, V value) : V
super K
+lastKey() : K
+subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) : ConcurrentNavigableMap<KV>
+headMap(K toKey, boolean inclusive) : ConcurrentNavigableMap<KV>
+tailMap(K fromKey, boolean inclusive) : ConcurrentNavigableMap<KV>
+subMap(K fromKey, K toKey) : ConcurrentNavigableMap<KV>
+headMap(K toKey) : ConcurrentNavigableMap<KV>
+tailMap(K fromKey) : ConcurrentNavigableMap<KV>
+lowerEntry(K key) : Entry<KV>
+lowerKey(K key) : K
+floorEntry(K key) : Entry<KV>
+floorKey(K key) : K
+ceilingEntry(K key) : Entry<KV>
+ceilingKey(K key) : K
+higherEntry(K key) : Entry<KV>
+higherKey(K key) : K
+firstEntry() : Entry<KV>
+lastEntry() : Entry<KV>
+pollFirstEntry() : Entry<KV>
+pollLastEntry() : Entry<KV>
Node<K,V>
key : K
value : Object
next : Node<KV>
-UNSAFE : Unsafe
-valueOffset : long
-nextOffset : long
~casValue(Object cmp, Object val) : boolean
~casNext(Node<K,V> cmp, Node<K,V> val) : boolean
~isMarker() : boolean
~isBaseHeader() : boolean
~appendMarker(Node<K,V> f) : boolean
~helpDelete(Node<K,V> b, Node<K,V> f)
~getValidValue() : V
~createSnapshot() : SimpleImmutableEntry<KV>
Unsafe
Index<K,V>
node : Node<KV>
down : Index<KV>
right : Index<KV>
-UNSAFE : Unsafe
-rightOffset : long
~casRight(Index<K,V> cmp, Index<K,V> val) : boolean
~indexesDeletedNode() : boolean
~link(Index<K,V> succ, Index<K,V> newSucc) : boolean
~unlink(Index<K,V> succ) : boolean
HeadIndex<K,V>
level : int
Map
Cloneable
Serializable
AbstractMap
package com.qupeng.concurrent.map;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class TestConcurrentSkipListMap {
public static void main(String args[]) {
ConcurrentSkipListMap<Integer, String> concurrentSkipListMap = new ConcurrentSkipListMap();
int i = 50;
while (i-- > 0) {
concurrentSkipListMap.put(i, String.valueOf(i));
}
StringBuilder stringBuilder = new StringBuilder();
Iterator<Map.Entry<Integer, String>> iterator = concurrentSkipListMap.entrySet().iterator();
while (iterator.hasNext()) {
stringBuilder.append(iterator.next().getKey()).append(",");
}
System.out.println(stringBuilder.toString());
stringBuilder = new StringBuilder();
ConcurrentNavigableMap concurrentNavigableMap = concurrentSkipListMap.descendingMap();
iterator = concurrentNavigableMap.entrySet().iterator();
while (iterator.hasNext()) {
stringBuilder.append(iterator.next().getKey()).append(",");
}
System.out.println(stringBuilder.toString());
}
}
并发集合(ConcurrentSkipListSet)
ConcurrentSkipListSet是线程安全的有序的集合,适用于高并发的场景。
ConcurrentSkipListSet应用场景对标TreeSet,它们都是有序的集合。但是,第一,它们的线程安全机制不同,TreeSet是非线程安全的,而ConcurrentSkipListSet是线程安全的。第二,ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,而TreeSet是通过TreeMap实现的。
ConcurrentSkipListSet
-m : ConcurrentNavigableMap<E,Object>
+clone() : ConcurrentSkipListSet<E>
+size() : int
+isEmpty() : boolean
+contains(Object o) : boolean
+iterator() : Iterator<E>
+toArray() : Object[]
+toArray(T[] a) : T[]
+add(E e) : boolean
+remove(Object o) : boolean
+containsAll(Collection<?> c) : boolean
+addAll(Collection<? extends E> c) : boolean
+retainAll(Collection<?> c) : boolean
+removeAll(Collection<?> c) : boolean
+clear()
+equals(Object o) : boolean
+lower(E e) : E
+floor(E e) : E
+ceiling(E e) : E
+higher(E e) : E
+pollFirst() : E
+pollLast() : E
+first() : E
+last() : E
+iterator() : Iterator<E>
+descendingSet() : NavigableSet<E>
+descendingIterator() : Iterator<E>
+subSet(E fromElement, boolean fromInclusive,E toElement, boolean toInclusive) : NavigableSet<E>
+headSet(E toElement, boolean inclusive) : NavigableSet<E>
+tailSet(E fromElement, boolean inclusive) : NavigableSet<E>
+subSet(E fromElement, E toElement) : SortedSet<E>
+headSet(E toElement) : SortedSet<E>
+tailSet(E fromElement) : SortedSet<E>
super E
NavigableSet
Cloneable
Serializable
AbstractSet
ConcurrentSkipListMap
1
1
写时复制集合
类结构
CopyOnWriteArrayList
lock : ReentrantLock
-array : Object[]
+CopyOnWriteArrayList(E[] toCopyIn)
+size() : int
+isEmpty() : boolean
+contains(Object o) : boolean
+iterator() : Iterator<E>
+toArray() : Object[]
+toArray(T[] a) : T[]
+add(E e) : boolean
+remove(Object o) : boolean
+containsAll(Collection<?> c) : boolean
+addAll(Collection<? extends E> c) : boolean
+addAll(int index, Collection<? extends E> c) : boolean
+removeAll(Collection<?> c) : boolean
+retainAll(Collection<?> c) : boolean
+clear()
+equals(Object o) : boolean
+hashCode() : int
+get(int index) : E
+set(int index, E element) : E
+add(int index, E element)
+remove(int index) : E
+indexOf(Object o) : int
+indexOf(E e, int index) : int
+lastIndexOf(Object o) : int
+lastIndexOf(E e, int index) : int
+listIterator() : ListIterator<E>
+listIterator(int index) : ListIterator<E>
+subList(int fromIndex, int toIndex) : List<E>
+clone() : Object
+addIfAbsent(E e) : boolean
+addAllAbsent(Collection<? extends E> c) : int
+toString() : String
COWIterator
snapshot : Object[]
cursor : int
COWSubList
CopyOnWriteArraySet
-al : CopyOnWriteArrayList<E>
+size() : int
+isEmpty() : boolean
+contains(Object o) : boolean
+iterator() : Iterator<E>
+toArray() : Object[]
+toArray(T[] a) : T[]
+add(E e) : boolean
+remove(Object o) : boolean
+containsAll(Collection<?> c) : boolean
+addAll(Collection<? extends E> c) : boolean
+retainAll(Collection<?> c) : boolean
+removeAll(Collection<?> c) : boolean
+clear()
+equals(Object o) : boolean
+hashCode() : int
List
RandomAccess
Cloneable
Serializable
AbstractSet
1
1
CopyOnWriteArrayList
CopyOnWriteArrayList这是一个ArrayList的线程安全的变体,其原理大概可以通俗的理解为:初始化的时候只有一个容器,很长一段时间,这个容器数据、数量等没有发生变化的时候,大家(多个线程),都是读取(假设这段时间里只发生读取的操作)同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,但是后来有人往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。
- 写数组的拷贝,支持高效率并发且是线程安全的,读操作无锁的ArrayList。所有可变操作都是通过对底层数组进行一次新的复制来实现。
- 适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差。
- 合适读多写少的场景,不过这类慎用 ,因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。
CopyOnWriteArrayList 有几个缺点: - 由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc。
(1、young gc :年轻代(Young Generation):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉(IBM的研究表明,98%的对象都是很快消亡的),这个GC机制被称为Minor GC或叫Young GC。 - 年老代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时,将执行Major GC,也叫 Full GC
) - 不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;
CopyOnWriteArrayList示例:
package com.qupeng.concurrent.list;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestCopyOnWriteArrayList {
public static void main(String[] args) throws InterruptedException {
// List<String> list = new ArrayList();
List<String> list = new CopyOnWriteArrayList<>();
Reader reader1 = new Reader(list);
Reader reader2 = new Reader(list);
Reader reader3 = new Reader(list);
Reader reader4 = new Reader(list);
Reader reader5 = new Reader(list);
Writer writer = new Writer(list);
ExecutorService executorService = Executors.newFixedThreadPool(6);
executorService.execute(reader1);
executorService.execute(reader2);
executorService.execute(reader3);
executorService.execute(reader4);
executorService.execute(reader5);
executorService.execute(writer);
Thread.sleep(5 * 1000);
reader1.isRunning = false;
reader2.isRunning = false;
reader3.isRunning = false;
reader4.isRunning = false;
reader5.isRunning = false;
writer.isRunning = false;
executorService.shutdownNow();
}
static class Writer implements Runnable {
private int value = 0;
private boolean isRunning = true;
private List<String> list;
public Writer(List<String> list) {
this.list = list;
}
@Override
public void run() {
while (isRunning) {
// System.out.println(String.format("%60sAdd to list : %s", " ", value++));
list.add(String.valueOf(value));
}
}
}
static class Reader implements Runnable {
boolean isRunning = true;
private List<String> list;
public Reader(List<String> list) {
this.list = list;
}
@Override
public void run() {
while (isRunning) {
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
System.out.println("Reader " + Thread.currentThread().getId() + " get from list : " + iter.next());
}
}
}
}
}
如果将CopyOnWriteArrayList换成普通的ArrayList,将会抛出如下异常:
Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-5" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at com.qupeng.concurrent.list.TestCopyOnWriteArrayList$Reader.run(TestCopyOnWriteArrayList.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
CopyOnWriteArraySet