1、ConcurrentHashMap
多线程HashMap put可能引起死循环。
- ConcurrentHashMap使用锁分段,将数据分段存储,每段都分配锁。
- 由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁,HashEntry用于存储键值对数据。
- 一个Segment里包含一个HashEntry数组。
- get除非读到空值才加锁重读。将要使用的共享变量都会定义成volatile类型。
- 插入元素前进行扩容判断。
- 不对整个容器扩容,只对某个segment进行扩容。
- 计算size时先尝试2次不锁segment统计,如果容器count发生了变化则会加锁统计size。
2、ConcurrentLinkedQueue
入队列
队列添加元素- tail节点并不总是尾节点。
- 通过tail节点来找出尾结点。
- 通过hops变量减少tail节点的更新频率,并不是每次节点入队后都将tail节点更新成尾结点。
出队列
- 只有当head节点里没有元素时,出队操作才更新head节点。
- 首先获取头结点元素,判断头结点元素是否为空,如果为空表示另一个线程已经进行了一次出队操作并取走,如果不为空,则尝试CAS置空。
3、阻塞队列
队列满时,队列会阻塞插入元素的线程,直到元素不满。
队列空时,队列阻塞获取元素的线程,直到队列变非空。
- ArrayBlockingQueue 有界
- LinkedBlockingQueue 有界
- PriorityBlockingQueue 无界
- DelayQueue 无界
- SynchronousQueue 不存储元素的无界队列,每一个put操作必须等待一个take操作,否则不能继续添加元素。
- LinkedTransferQueue 如果当前有消费者正在等待接收元素,transfer方法可以把生产者传入的元素立刻传输给消费者。
- LinkedBlockingDeque 双向阻塞队列
4、Fork Join
- 工作窃取算法,某个线程从其他队列里窃取任务来执行。窃取任务会获取双端队列尾部的任务。
- 当调用fork时,程序会调用ForkJoinWorkerThread的pushTask方法异步执行这个任务。 ForkJoinPool会唤醒或创建一个工作线程来执行任务。ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成。
- Join方法的主要作用是阻塞当前线程并等待结果。
public class CountTask extends RecursiveTask<Integer> {
private static final int THRESHOLD = 2;
private int start;
private int end;
public CountTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
boolean canCompute = (end - start) <= THRESHOLD;
if (canCompute) {
for (int i = start; i <= end; i++) {
sum += i;
}
} else {
int middle = (start + end) / 2;
CountTask leftTask = new CountTask(start, middle);
CountTask rightTask = new CountTask(middle + 1, end);
leftTask.fork();
rightTask.fork();
int leftResult = leftTask.join();
int rightResult = rightTask.join();
sum = leftResult + rightResult;
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
CountTask task = new CountTask(1, 4);
Future<Integer> result = forkJoinPool.submit(task);
try {
System.out.println(result.get());
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
}
}
5、原子类操作
- 原子更新引用类型:AtomicReference、AtomicReferenceFieldUpdater、AtomicMarkableReference
public class AtomicReferenceTest {
public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getOld());
}
public static class User {
private String name;
private int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
- 原子更新字段类
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference
原子更新字段类都是抽象类,每次使用时必须使用静态方法newUpdater()创建一个更新器。更新类的字段必须使用public volatile修饰符。
public class AtomicIntegerFieldUpdaterTest {
private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
public static void main(String[] args) {
User conan = new User("conan", 10);
System.out.println(a.getAndIncrement(conan));
System.out.println(a.get(conan));
}
public static class User {
private String name;
public volatile int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
网友评论