Java并发编程艺术(五) Java并发容器和框架

1、ConcurrentHashMap

多线程HashMap put可能引起死循环。

https://blog.csdn.net/qq_35958391/article/details/125015642

  • ConcurrentHashMap使用锁分段,将数据分段存储,每段都分配锁。
  • 由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁,HashEntry用于存储键值对数据。
  • 一个Segment里包含一个HashEntry数组。
ConcurrentHashMap
  • 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 join
  • 当调用fork时,程序会调用ForkJoinWorkerThread的pushTask方法异步执行这个任务。 ForkJoinPool会唤醒或创建一个工作线程来执行任务。ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成。
  • Join方法的主要作用是阻塞当前线程并等待结果。
public class CountTask extends RecursiveTask {

    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 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 atomicUserRef = new AtomicReference();

    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 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;
        }
    }
}

你可能感兴趣的:(Java并发编程艺术(五) Java并发容器和框架)