• Concurrent包详解及使用场景


    Concurrent包是jdk1.5所提供的一个针对高并发进行编程的包。

    1.阻塞式队列 - BlockingQueue

    遵循先进先出(FIFO)的原则。阻塞式队列本身使用的时候是需要指定界限的。

    在生产者消费者模型中,生产数据和消费数据的速率不一致,如果生产数据速度快一些,消费(处理)不过来,就会导致数据丢失。这时候我们就可以应用上阻塞队列来解决这个问题。

    ArrayBlockingQueue - 阻塞式顺序队列

    底层是基于数组来进行存储,使用的时候需要指定一个容量,容量指定之后不可改变。--- 生产 - 消费模型

        public static void main(String[] args) throws InterruptedException {
    
            // 这个队列在创建的时候需要指定容量,容量在指定之后不可变
            ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
    
            // 添加队列
            queue.add("a");
            queue.add("b");
            queue.add("c");
            queue.add("d");
            queue.add("e");
    
            // 如果队列已满,则抛出异常 - IllegalStateException
            // queue.add("a");
            // 返回值标记元素是否成功添加到队列里面
            // 如果队列已满,则返回false
            // boolean b = queue.offer("b");
            // System.out.println(b);
            // 如果队列已满,会产生阻塞 --- 直到这个队列中有元素被取出,才会放开阻塞
            // queue.put("c");
    
            // 定时阻塞
            // 在3s之内如果有元素被取出,那么元素就会添加到队列中
            // 如果3s之后队列依然是满的,那么返回false表示添加失败
            boolean b = queue.offer("d", 3000, TimeUnit.MILLISECONDS);
            System.out.println(b);
            System.out.println(queue);
        }

    LinkedBlockingQueue - 阻塞式链式队列

    底层是基于链表(节点)来进行数据的存储。在使用的时候可以指定初始容量,也可以不指定。

    如果指定了容量,就以指定的容量为准来进行存储;

    如果不指定容量,那么默认容量是 Integer.MAX_VALUE -> 231 - 1。如果不指定容量,一般认为这个容量是无限的。

    PriorityBlockingQueue - 具有优先级的阻塞式队列

    如果不指定容量,默认容量是11.如果将元素取出,那么会对元素进行自然排序 --- 要求存储的对象所对应的类必须实现Comparable,重写compareTo方法,讲比较规则写到方法中;如果进行迭代遍历,那么不保证排序。

    SynchronousQueue - 同步队列

    只允许存储1个元素。

    2.并发映射 - ConcurrentMap

    HashMap - 底层依靠数组+链表存储的数据

    默认初始容量是16,默认加载因子是0.75f,默认扩容每次增加一倍。本身是一个异步式线程不安全的映射

    Hashtable - 同步式线程安全的映射

    对外提供的方法都是同步方法

    ConcurrentHashMap - 异步式线程安全的映射

    在jdk1.8之前,采用分段(分桶)锁, 分段锁采用的是读写锁机制(读锁:允许多个线程读,但是不允许线程写;写锁:允许一个线程写,但是不允许线程读);jdk1.8不再采用锁机制,而是CAS(Compare and Swap)算法, 减小了锁的开销;如果一个桶中的元素个数超过了8个,那么会将这个桶的链表扭转成一棵红黑树(自平衡二叉查找树)结构。

    ConcurrentNavigableMap - 并发导航映射

    本身是一个接口,所以更多的是使用实现类

    ConcurrentSkipListMap - 并发跳跃表映射

    跳跃表:为了提高查询效率所产生的一种数据结构

    跳跃表是典型的以空间换时间的产物。

    跳跃表的时间复杂度是O(logn)

    如果跳跃表中插入新的元素,新的元素是否往上提取,遵循“抛硬币”原则 --- 1/2原则

    只要保证这个节点有一半的概率被提取就可以

    跳跃表适合大量查询而不增删的场景

    CountDownLatch - 闭锁 - 线程减锁

    对线程neg进行计数,当计数归零的时候会开放阻塞线程继续往下执行

    public class CountDownLatchDemo {
        
        public static void main(String[] args) throws InterruptedException {
            
            CountDownLatch cdl = new CountDownLatch(5);
            
            new Thread(new Teacher(cdl)).start();
            new Thread(new Student(cdl)).start();
            new Thread(new Student(cdl)).start();
            new Thread(new Student(cdl)).start();
            new Thread(new Student(cdl)).start();
            
            // 表示让线程阻塞,直到计数归零的时候阻塞才能放开
            cdl.await();        
            System.out.println("考试结束~~~");      
        }
    }
    
    class Student implements Runnable {
        
        private CountDownLatch cdl;
    
        public Student(CountDownLatch cdl) {
            this.cdl = cdl;
        }
    
        @Override
        public void run() {
            
            System.out.println("学生来到考场,准备考试~~~");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("学生交卷离开考场");
            
            // 计数-1
            cdl.countDown();
        }
    }
    
    class Teacher implements Runnable {
        
        private CountDownLatch cdl;
    
        public Teacher(CountDownLatch cdl) {
            this.cdl = cdl;
        }
    
        @Override
        public void run() {
    
            System.out.println("老师来到考场,准备考试~~~");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("老师收卷离开了考场~~~");
            
            cdl.countDown();
        }
    }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    万恶的 one or more listeners failed to start 和 Servlet.init() for servlet [dispatcherServlet] threw exception
    实验四 主存空间的分配和回收
    实验二 作业调度模拟程序
    实验一
    实验三
    【安卓】实验7 BindService模拟通信
    计时器页面设计
    实验五 存储管理实验
    实验6 在应用程序中播放音频和视频
    实验5数独游戏界面设计
  • 原文地址:https://www.cnblogs.com/chuijingjing/p/10072912.html
Copyright © 2020-2023  润新知