美文网首页
Java并发编程艺术(五) Java并发容器和框架

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

作者: kaiker | 来源:发表于2022-09-17 15:26 被阅读0次

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

    相关文章

      网友评论

          本文标题:Java并发编程艺术(五) Java并发容器和框架

          本文链接:https://www.haomeiwen.com/subject/twteortx.html