并发集合(java.util.concurrent)
一、List
CopyOnWriteArrayList(ReentrantLock实现线程安全)
(1)并发修改(写操作)时保证线程安全:
通过ReentrantLock实现多个线程并发修改时的线程安全同步(添加元素的同时,不允许删除)
添加新元素:list.add("")
按照指定下标替换元素:list.set(index, element)
按照指定下标删除元素:list.remove(0)
(2)并发读取:
没有加锁,允许多个线程同时并发读取;但是读取时,可能产生脏读(读取的同时,允许写入操作)。
(3)CopyOnWrite思想:修改时将原数组内容复制Copy到新数组内,在新数组内修改,然后替换
二、Set
CopyOnWriteArraySet(ReentrantLock实现线程安全)
内部通过一个CopyOnWriteArrayList实现。
三、Queue
BlockingQueue阻塞队列(ReentrantLock实现线程安全)
阻塞队列:由两个线程,分别进行读写(take和put)操作;读取时,不允许写入,如果队列为空,则读取线程阻塞;写入时,不允许读取,如果队列已满,则写入线程阻塞。
ArrayBlockingQueue:有界队列
LinkedBlockingQueue:无界队列
应用:生产者-消费者模型
// 生产者-消费者模型
public class Test01 {
public static void main(String[] args) {
// 自增ID (原子性)
AtomicInteger number = new AtomicInteger(1);
// 保存生产者数据的阻塞队列
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(); // LinkedBlockingQueue<>(3) 阻塞队列最多保存3个生产者数据
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
6, 6, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
// 1个生产者线程
executor.execute(() -> {
// 生产10个数据
for (int i = 1; i <= 10; i++) {
String data = "数据" + number.getAndIncrement();
System.out.println("【生产者】生成:" + data);
try {
// 保存至阻塞队列
queue.put(data); // 入队
// 模拟延迟
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 5个消费者线程
for (int i = 1; i <= 5; i++) {
executor.execute(() -> {
while (true){
try {
String data = queue.take();
System.out.println("【消费" + Thread.currentThread().getName() + "】获取:" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
四、Map
ConcurrentHashMap
(1)JDK1.7:通过分段锁实现线程安全。
(2)JDK1.8:通过 synchronized+CAS实现线程安全。
当产生哈希冲突时,通过synchronized将根节点(链表头节点或树根节点)作为锁,进行线程的同步安全;
在没有产生哈希冲突时,通过CAS进行无锁化操作,降低synchronized进行线程同步操作所引发的性能下降。