Set代表无序的,元素不可重复的集合;
List代表有序的,元素可以重复的集合;
Queue代表先进先出(FIFO)的队列;
Map代表具有映射关系(key-value)的集合。
这些接口拥有众多的实现类,其中最常用的实现类有HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap、TreeMap等。
读写分离,空间换时间,避免为保证并发安全导致的激烈的锁竞争。
CopyOnWriteArrayList 相对于 ArrayList 线程安全,底层通过复制数组的方式来实现,其核心概念就是: 数据读取时直接读取,不需要锁,数据写入时,需要锁,且对副本进行操作。那么当数据的操作以读取为主时,我们便可以省去大量的读锁带来的消耗。
为了能让多线程操作List时,一个线程的修改能被另一个线程立马发现,CopyOnWriteList采用了Volatile关键词来进行修饰,即每次数据读取不从缓存里面读取,而是直接从数据的内存地址中读取。 只要把最新的线程对它写操作,其他线程立马可以看到最新的数据
当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空。
有界阻塞队列,底层采用数组实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不能保证线程访问队列的公平性,参数fair可用于设置线程是否公平访问队列。为了保证公平性,通常会降低吞吐量
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ArrayBlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue queue = new ArrayBlockingQueue<>(10);
for (int i = 0; i < 10; i++) {
queue.put(i);
}
System.out.println("队列已满,添加元素会阻塞");
for (int i = 0; i < 5; i++) {
System.out.println(queue.take());
}
System.out.println("队列已空,取元素会阻塞");
for (int i = 0; i < 10; i++) {
queue.put(i);
}
}
}
这个例子中,我们创建了一个大小为10的ArrayBlockingQueue,并向其中添加了10个元素。当队列已满时,添加元素会阻塞。当队列为空时,取元素会阻塞
LinkedBlockingQueue是一个用单向链表实现的有界阻塞队列,可以当做无界队列也可以当做有界队列来使用。通常在创建 LinkedBlockingQueue 对象时,会指定队列最大的容量。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。与 ArrayBlockingQueue 相比起来具有更高的吞吐量。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class LinkedBlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue queue = new LinkedBlockingQueue<>(10);
for (int i = 0; i < 10; i++) {
queue.put(i);
}
System.out.println("队列已满,添加元素会阻塞");
for (int i = 0; i < 5; i++) {
System.out.println(queue.take());
}
System.out.println("队列已空,取元素会阻塞");
for (int i = 0; i < 10; i++) {
queue.put(i);
}
}
}
这个例子中,我们创建了一个默认大小的LinkedBlockingQueue,并向其中添加了10个元素。当队列已满时,添加元素会阻塞。当队列为空时,取元素会阻塞 。
支持优先级的无界阻塞队列。默认情况下元素采取自然顺序升序排列。也可以自定义类实现compareTo()方法来指定元素排序规则,或者初始化PriorityBlockingQueue时,指定构造参数Comparator来进行排序。
PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会自动扩容。
PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。
public class Test2 {
public static void main(String args[]) throws InterruptedException {
BlockingQueue queue = new PriorityBlockingQueue<>();
queue.put(3);
queue.put(4);
queue.put(2);
queue.put(1);
queue.put(0);
for (int i = 0; i < 5; i++) {
System.out.println(queue.take());
}
}
}
运行结果:
支持延时获取元素的无界阻塞队列。队列使用PriorityBlockingQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
public class Test2 {
public static void main(String args[]) throws InterruptedException {
BlockingQueue queue = new DelayQueue<>();
for (int i = 0; i < 10; i++) {
queue.put(new DelayedElement(i, 1000));
}
System.out.println("队列已满,添加元素会阻塞");
for (int i = 0; i < 5; i++) {
System.out.println(queue.take());
}
System.out.println("队列已空,取元素会阻塞");
for (int i = 0; i < 10; i++) {
queue.put(new DelayedElement(i, 1000));
}
}
static class DelayedElement implements Delayed {
private int value;
private long delayTime;
public DelayedElement(int value, long delayTime) {
this.value = value;
this.delayTime = System.currentTimeMillis() + delayTime;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "DelayedElement{" +
"value=" + value +
", delayTime=" + delayTime +
'}';
}
}
}
运行结果:
不存储元素的阻塞队列,每一个put必须等待一个take操作,否则不能继续添加元素。支持公平访问队列。
SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。
public static void main(String args[]) throws InterruptedException {
BlockingQueue queue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println("线程1开始放入元素");
queue.put(1);
System.out.println("线程1放入元素成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("线程2开始取出元素");
Integer element = queue.take();
System.out.println("线程2取出元素成功:" + element);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
运行结果:
由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,多了tryTransfer和transfer方法。
transfer方法:如果当前有消费者正在等待接收元素(take或者待时间限制的poll方法),transfer可以把生产者传入的元素立刻传给消费者。如果没有消费者等待接收元素,则将元素放在队列的tail节点,并等到该元素被消费者消费了才返回。
tryTransfer方法:用来试探生产者传入的元素能否直接传给消费者。如果没有消费者在等待,则返回false。和上述方法的区别是该方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
public class LinkedTransferQueueExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue queue = new LinkedTransferQueue<>();
new Thread(() -> {
try {
System.out.println("线程1开始放入元素");
queue.transfer(1);
System.out.println("线程1放入元素成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("线程2开始取出元素");
Integer element = queue.take();
System.out.println("线程2取出元素成功:" + element);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
运行结果:
线程1开始放入元素
线程2开始取出元素
线程1放入元素成功
线程2取出元素成功:1