前言: volatile(可见性、有序性)
当变量被vilatile修饰时,对共享变量的读操作都在主内存中进行,其它线程进行修改会立即刷入主内存中。
Java中对引用类型变量的基本的读取、赋值是原子性的。
X=10: 原子性,
Y=X: 非原子性,
Y++: 非原子性,
z=z+1: 非原子性,
一、阻塞队列
方法名 | 描述 | 返回值 |
---|---|---|
add() | 队尾添加元素,成功返回 true ,失败抛出异常。 | boolean |
offer() | 队尾新添加元素。返回true表示添加成功,false表示添加失败,不会抛出异常(可延时操作)。 | boolean |
put() | 向队列末尾新添加元素,如果队列已满,当前线程就等待(阻塞线程),响应中断异常。 | void |
remove() | 移除头部元素,如果为null则抛出异常,也可移除已知元素。 | boolean |
poll() | 移除头部元素并返回。如果null不会抛异常,会返回null(可延时操作)。 | E |
element() | 获取头部元素,如果是null,抛出异常,不会删除元素。 | E |
peek() | 获取头部元素,不会删除元素。 | E |
take() | 如果队列为空则等待,否则移除头元素。 | E |
remainingCapacity() | 还剩多少空间。 | int |
ArrayBlockingQueue
数组结构组成的有界阻塞队列,创建时需指定大小。
LikedBlockingQueue
链表结构组成的有界队列,按照FIFO原则对元素排序,默认长度为Integer.MAX_VALUE。
PriorityBlockingQueue
支持线程优先级排序的无界队列,默认自然排序,也可实现compareTo()方法排序。
DelayQueue
延时获取的无界队列,创建元素时可指定多久后才可以从队列中获取元素。
SynchronousQueue
不储存元素,每次put必须等待take,否则不能添加元素,支持公平锁和非公平锁。
LinkedBlockingDeque
链表结构组成的双向阻塞队列,支持头尾操作。
addFirst() | addLast() |
getFirst() | getLast() |
peekFirst() | peekLast() |
offerFirst() | offerLast() |
LinkedTransferQueue
链表构成的无界队列,多了transfer()、tryTransfer()在队尾插元素方法。
在队尾插元素,没有被消费,则等待。
queue.transfer("transfer");
queue.tryTransfer("tryTransfer");
二、Atomic类
AtomicInteger
内部依赖Unsafe类(由C++实现,内部存在大量汇编CPU指令等代码,),Unsafe的compareAndSwapInt采用CAS算法(会造成ABA问题),包含三个参数: 内存值V、旧的预期值A。要修改的新值B,
当A等于V时才修改(属于乐观锁)。
get() | 获取 | int |
---|---|---|
set() | 设置新值 | void |
getAndIncrement() | 获取、加一 | int |
addAndGet() | 加值、获取 | int |
compareAndSet(10,20) | 根据比较结果设置 | boolean |
doubleValue() | 转换为double | double |
AtomicLong
与AutomicInteger相似,Unsafe的compareAndSwapLong多了SUPPORTS_NATIVE_CX8用来判断机器硬件和JVM版本是否支持8字节数字的cmpxchg操作,不支持则加锁(synchronized)保证原子性。
get() | 获取 | long |
---|---|---|
set() | 设置新值 | void |
getAndSet() | 获取、更新 | long |
getAndIncrement() | 获取、加一 | long |
decrementAndGet() | 获取、减一 | long |
addAndGet() | 加值、获取 | long |
compareAndSet(10,20) | 根据比较结果设置 | boolean |
doubleValue() | 转换为double | double |
AtomicBoolean
内部本身用volatile修饰的int型成员变量。
get() | 获取值 | boolean |
---|---|---|
set() | 更新 | void |
getAndSet() | 获取、设值 | boolean |
AtomicIntegerArray
set(0,10) | 设值 | void |
---|---|---|
get(0) | 取值 | int |
addAndGet(0,2) | 加值、获取 | int |
decrementAndGet(0) | 减一、获取 | int |
incrementAndGet(0) | 加一、获取 | int |
getAndIncrement(0) | 获取、加一 | int |
getAndDecrement(0) | 获取、减一 | int |
updateAndGet(0,(i)->i+1) | 通过lambda 自由更改值 | int |
AtomicReference
提供对象引用非阻塞原子性读写操作,CSA乐观方式。
import java.util.concurrent.atomic.AtomicReference;
public class Test005 {
public static void main(String[]args){
AtomicReference<Simple> atomic =new AtomicReference<>(new Simple("灭霸",18));
Simple simple1 = atomic.get();
System.out.println(simple1);
atomic.set(new Simple("哥斯拉",19));
Simple simple2 = atomic.getAndSet(new Simple("葫芦娃",22));
}
static class Simple{
private String name;
private int age;
public Simple(){}
public Simple(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Simple{" +
"名字='" + name + '\'' +
", 年龄=" + age +'}';
}
}
}
三、并发集合
1、CurrentMap(并发映射)
ConcurrentHashMap(代替HashMap)
1.8前分段(默认16个segement)锁机制,get不加锁用volatile保证可见性,put加锁控制在小范围内。
1.8后摒弃segement,直接用bucket,当链表长度超过8时转为红黑树,降低查询时间复杂度,采用CAS+synchronized保证安全性。底层数据结构为数组+链表/红黑树。
ConcurrentSkipListMap(代替TreeMap)
使用红黑树按照key的顺序(自然顺序、自定义顺序)来使得键值对有序存储的底层是通过跳表来实现的。
ConcurrentSkipListSet(代替TreeSet)
基于 ConcurrentSkipListMap 实现的,ConcurrentSkipListMap的键就不重复。
ConcurrentLinkedQueue(BlockingQueue)
通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLikedQueue性能好于BlockingQueue。
ConcurrentLinkedDeque
ConcurrentLinkedQueue的升级版,非阻塞,无锁,无界 ,线程安全双端操作的队列。
2、CopyOnWrite(COW算法的容器)
CopyOnWrite容器读采用写分离思想,写时会先用显式锁整个容器(防止其它写线程),然后拷贝一份副本,对副本操作,读线程访问原容器数据。
内存开销大,实时数据一致性不高。适用于读远大于写操作,数据一致性要求不高的场景。
CopyOnWriteArrayList(替代ArrayList)
利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值。
CopyOnWriteArraySet
基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法, 其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。