高并发Concurrent包(心得)

  1. 概述:Concurrent包是JDK1.5提供的一个用于应对高并发的包,包含5块主要内容:BlockingQueue、 ConcurrentMap、 ExecutorService、 Lock、 Atomic操作
  2. 阻塞式队列-BlockqingQueue
    1. 概述:BlockqingQueue是阻塞式队列中的顶级接口,BlockingQueue一般是有界限的,在队列为空的时候进行获取操作会产生阻塞,在队列已满的情况下继续存储元素会产生阻塞,遵循先进先出(FIFO)的原则,适用于生产消费模型,BlockingQueue中不允许存储null,适合生产消费
    2. ArrayBlockingQueue-阻塞式顺序队列
      1. 在使用的时候需要指定容量/界限,固定大小不变
      2. 底层基于数组来存储数据
      3. 容量指定以后不能改变
      4. 遵循先进先出(FIFO)的原则
    3. LinkedBlockingQueue-阻塞式链式队列
      1. 在使用的时候可以指定容量也可以不指定容量
      2. 如果不指定容量,则容量默认为Integer.MAX_VALUE,此时认为容量是无限的

      3. 如果指定容量,则容量指定之后不可变

      4. 底层基于节点(链表)来存储数据

      5. 遵循先进先出(FIFO)的原则

    4. PriorityBlockingQueue-阻塞式优先级队列

      1. 在使用的时候可以指定容量也可以不指定容量,不指定容量则默认为11,如果指定容量,则指定后不可以改变,最大不能超过Integer.MAX_VALUE-8

      2. PriorityBlockingQueue要求存储的元素对应的类必须实现Comparable接口,重写其中的compareTo方法来指定比较规则,在存储元素的时候会根据指定的比较规则对元素进行排序,在迭代的时候不保证元素的排序顺序

        public class PriorityBlockingQueueDemo {
         
            public static void main(String[] args) throws Exception {
         
                // 创建队列
                PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>();
         
                // 添加元素
                queue.put(new Student("Amy", 16));
                queue.put(new Student("Bob", 25));
                queue.put(new Student("Cathy", 20));
                queue.put(new Student("David", 13));
         
                // 遍历队列
                // 需要注意的是,如果想要拿到排序的结果,不能以迭代的方法获取
                for (int i = 0; i < 4; i++) {
                    System.out.println(queue.take());
                }
         
            }
         
        }
         
        class Student implements Comparable<Student> {
         
            private String name;
            private int age;
         
            public Student(String name, int age) {
                super();
                this.name = name;
                this.age = age;
            }
         
            @Override
            public String toString() {
                return "Student [name=" + name + ", age=" + age + "]";
            }
         
            // 在这个方法中指定比较规则
            // 根据学生的年龄进行升序排序
            @Override
            public int compareTo(Student o) {
                return this.age - o.age;
            }
         
        }
        

         

    5. SynchronousQueue-同步队列

      1. 容量默认为1,并且只能为1,因此只能存储1个元素,如果该队列已有一个元素,则试图向队列中新添一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走,如果该队列为空,则试图从队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中添加了一个新的元素,一般会将同步队列称之为是数据的汇合点

    6. BlockingDueue-阻塞式双向队列

      1. BlockingDeque继承了BlockingQueue

      2. 在使用的时候,也是需要指定容量的,该队列称之为双向队列,即队列的两端都可以添加元素,也可以从队列的两端获取元素

  3. ConcurrentMap-并发映射

    1. 概述:是JDK1.5提供的一套用于应对高并发的映射,并且在并发过程中能保证线程的安全

    2. ConcurrentHashMap-并发哈希映射

      1. 底层基于数组+链表结构

      2. 初始容量为16,加载因子是0.75,默认扩容是增加一倍

      3. ConcurrentHashMap采用的是分段/桶锁机制,来保证读写效率

      4. ConcurrentHashMap在后续版本中,在分段锁的基础上引入了读写锁机制:
        1. 读锁:允许多个线程读,不允许线程写
        2. 写锁:只允许一个线程写,不允许线程读
      5. 在JDK1.8中,为了避免锁所带来的开销,引入了CAS(Compare And Swap, 比较和交换)无锁算法。CAS需要和计算机具体的内核架构相关

      6. 在JDK1.8中,ConcurrentHashMap为了提高效率引入了红黑树机制

      7. 红黑树:
        1. 本质上是一棵自平衡二叉查找树
        2. 二叉查找树 - BST - Binary Search Tree
          1. 可以将二叉查找树理解为二分查找的空间结构
          2. 要求左<根<右
        3. 特点:
          1. 所有节点非红即黑
          2. 根节点为黑
          3. 红节点的子节点一定为黑,黑节点的子节点可以为黑可以为红
          4. 最下层的叶子节点一定是黑色的空节点
          5. 从根节点到任意一个叶子节点所经过的路径中的黑色节点个数要一致,既黑色节点高度是一致的
          6. 新添的节点颜色为红
    3. Co'oncurrentNavigableMap-并发导航映射
      1. 提供了用于截取子映射的方法

      2. 本身是一个接口,所以使用的是实现类ConcurrentSkipListMap - 并发跳跃表映射

      3. ConcurrentSkipListMap底层基于跳跃表来实现的

      4. 跳跃表:
        1. 适合于查询多的场景
        2. 要求元素必须有序
        3. 跳跃表允许层层提取,但是最上层的跳跃表中的元素个数至少是2个
        4. 跳跃表是一个典型的以空间换时间的产物
        5. 在跳跃表中,新添的元素是否提取到上层的跳跃表中,遵循"抛硬币"原则
        6. 跳跃表的时间复杂度是O(logn)
    4. ExecutorService-执行器服务
      1. 概述
        1. 线程池的意义:为了避免大量线程的创建和销毁,做到线程的复用
        2. 线程池在定义好之后,里面没有任何线程
        3. 每过来一个请求,会创建一个核心线程去处理这个请求
        4. 核心线程在处理完请求之后并不会被销毁,而是会等待下一个请求
        5. 只要核心线程没有达到指定的数量,那么过来的每一个请求都会去创建一个新的核心线程
        6. 当核心线程被全部占用,则后来的请求会被放入工作队列中,工作队列本质上是一个阻塞式队列
        7. 如果工作队列满了,则后来的请求会交给临时线程处理
        8. 临时线程在处理完请求之后,存活指定的一段时间,这段时间内如果没有新的请求则该临时线程被销毁
        9. 即使临时线程有空闲,也不会从工作队列中获取请求执行
        10. 如果临时线程被全部占用,后来的请求会被交给拒绝执行处理器进行拒绝处理
        11. ScheduledExecutorService:定时执行器服务。在Java中,很多定时机制的底层用的就是这个线程池

      2. Callable

        1. Callable的泛型表示的返回值类型,会将结果封装成一个Future对象

        2. Runnable和Callable的区别:

           RunableCallable
          返回值没有通过泛型规定返回值的类型
          启动方式通过Thread启动 , 通过线程池调用execute或者submit启动

          只能通过线程池的submit方法来启动

          容错机制

          不允许抛出异常,也就不能使用全局容错机制

          允许抛出异常,所以可以使用全局容错机制,例如Spring中的异常通知

           

      3. 分叉合并池

        1. 分叉:将一个大的任务分割成多个小的任务,每个小的任务都对应一个线程
        2. 合并:将分出去的小任务的计算结果进行汇总
        3. 分叉合并能够有效的提高CPU的利用率
        4. 当数据量越大的时候,分叉效率越高
        5. 分叉合并为了避免慢任务带来的效率降低问题,采用了"work-stealing"(工作窃取)策略来解决,即当一个核上的任务执行完成之后,这个核并不会空闲下来,而是会随机的扫描一个核,然后从这个核的任务队列尾端偷取一个任务回来执行
    5. Lock-锁
      1. 概述:synchronized虽然能通过锁对象来保证线程安全的问题,但是这个关键字在使用的时候并不灵活,因此在JDK1.5中,提供了一个单独的接口Lock
      2. 公平和非公平策略:
        1. 公平策略下抢占的是入队顺序,非公平策略下抢占的资源使用
        2. 非公平策略的效率会更高
        3. 默认情况下,锁采用的非公平策略
      3. 读写锁:ReadWriteLock - ReentrantReadWriteLock

      4. 其它:

        1. CountDownLatch:闭锁/线程递减锁。对线程计数,当被计数的线程结束之后可以开启其他的任务
        2. CyclicBarrier:栅栏。所有的线程到达一个点之后再分别继续执行
        3. Exchanger:交换机。用于交换两个线程之间的信息
        4. Semaphore:信号量。用于进行计数阻塞的。当有限的资源被全部占用的时候,后续的线程就需要阻塞,直到有资源被释放,则后续的线程可以去抢占执行。信号量在实际开发中的作用适用于进行限流
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宰祖宣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值