2020-11-30

高并发JUC

首先了解JUC干什么的,是JDK1.5中提供的一套并发包及其子包
一.说到JDK我们可以了解一下JDK几个重要的发展史:
● jdk1.2
产生Collection接口的根接口
● jdk1.5
反射机制、动态代理->代理模式、工厂模式->内省
迭代器->迭代模式
泛型
我们的JUC也是在jdk1.5产生的
● jdk1.8
Lambda表达式
函数式接口
时间包–java.time
● jdk11
modularity—目标(明确的依赖配置、封装增强、可变的JDK文件大小)
JShell–java编程语言和Java代码原型的交互式工具
语法糖
二.JUC包含的5个接口
BlockingQueue 阻塞式队列
ConcurrentMap 并发映射
ExecutorService 执行器服务(我们可以理解为线程池)
Lock 锁机制
Atomic 原子性操作
三、BlockingQueue-阻塞式队列
● 特征:1.阻塞、FIFO(先进先出原则)
2.BlockingQueue不能扩容
3.当队列放满,试图放入元素线程会被阻塞,当队列为空获取元素获取不到线程也会进入阻塞
4.阻塞队列中不允许元素为null
BlockingQueue常见实现类
1.ArrayBlockingQueue-阻塞式顺序队列:
a.底层依靠数组节点来存储数据
b. 在使用的时候需要指定容量
2.LinkedBlockingQueue-阻塞式链式队列
a.底层依靠单向节点来存储数据-因为是队列
b.在使用的时候可以指定容量也可以不指定,指定了容量不可变,没指定Integer.MAX_VALUE_,即2的31次方-1,因此这个容量相对较大,一般认为队列是无限的
3.PriorityBlockingQueue-具有优先级的阻塞式队列
a.底层依靠数组来存储数据
b.PriorityBlockingQueue会对放入其中的元素进行自然排序,如果要求元素按指定排序,对应的类必须实现Comparable接口,覆盖compareTo方法
c.如果需要给队列来单独指定比较规则,那么可以传入Comparator对象
4.SynchronousQueue-同步队列
a.这个队列不需要指定容量,容量默认为1且只能为1
四.ConcurrentMap-并发映射
●概述:ConcurrentMap是JDK1.5提供高并发、保护数据安全的映射机制
●***ConcurrentMap包含:
ConcurrentHashMap 并发哈希映射
ConcurrentNavigableMap 并发导航映射***
ConcurrentHashMap–并发哈希映射
1.CocurrentHashMap底层是基于数组+链表来存储数据
2.如果不指定,默认情况下,数组的初始容量为16,默认加载因子是0.75,扩容默认是增加一倍
3.ConcurrentHashMap的最大容量(桶数)是2的30 次方
4.JDK1.8开始ConcurrentMap引入红黑树机制
a.桶中元素>=8个时转成一棵红黑树;红黑树中的节点个数<7时,红黑树会扭转回链表
b.启用红黑树机制的最小容量为64,即桶数需要达到64个的时候才会使用红黑树
c.红黑树:
(1)本质上是一种自平衡二叉查找树
(2)二叉查找树特征:左子树小于根,右子树大于根,没有相等的节点
(3)红黑树的特征:
<1:所有节点非红即黑
<2:根节点必须是黑节点
❤️:红节点的子节点必须是黑色的
<4:最底层的叶子节点必须是黑色的空节点
<5:从根节点到任意一个叶子节点所经过的路径中的黑节点个数一致 ,即黑节点高度是相同的
<6:新添的节点颜色一定是红色的
<7:标准红黑色示意图
在这里插入图片描述
(4)红黑树的修正:
<1:前提:父子节点为红
<2:涂色:叔父节点为红,那么需要将父节点和叔父节点涂黑,将祖父节点涂红
❤️:左旋:叔父节点为黑,且当前节点为右子叶,需要以当前节点为轴进行左旋
<4:右旋:叔父节点为黑,且当前节点为左子叶,需要以父节点为轴进行右旋
(5)红黑树中,每添加一个元素都需要考虑是否要进行修正
(6)红黑树的时间复杂度是O(logn)
d. ConcurrentHashMap是一个异步线程安全的映射–支持并发
e.线程在使用锁的时候,会带来非常大的开销(线程状态的切换,线程的上下文的调度,cpu资源的切换)
f.ConcurrentHashMap的用法和HashMap一样,只有底层实现过程不一样之前使用HashMap的地方都可以替换为ConcurrentHashMap
ConcurentNavigableMap - 并发导航映射
1.ConCurrentNavigableMap用于截取子映射的方法
a.headMap–开始截取指定位置
在这里插入图片描述
b.tailMap–指定位置到尾部
在这里插入图片描述
c.subMap–获取指定部分数据
在这里插入图片描述
2.ConCurrentNavigableMap
a.实现类ConcurrentSkipListMap
b.并发跳跃表映射 ----底层基于了跳跃表来实现的
3.跳跃表–Skip List
a.针对有序列表来使用
b.适合用于读多写少场景
c.跳跃表可以进行多层提取,但是最后一层不得少于2个
d.典型的以空间换时间的产物
e.当新增元素的时候,这个元素是否要提取到上层跳跃表中遵循“抛硬币”的原则
f.跳跃表的时间复杂度是o(logn),空间复杂度是O(n)
五.ExcutorService–执行器服务
● 本质上:就是线程池
● 作用:减少线程的创建和销毁,减少服务器资源的浪费,做到线程池得到复用
●线程池:
1.流程图:
核心线程---工作队列---临时线程---拒绝处理器
2. a.线程池判断核心线程是否已经满了,否 则会创建线程执行任务,是则创建工作队列存储线程
b.线程池判断工作队列是否满了,否 把将要执行的任务加入队列,是则创建临时线程存储线程
c.线程池判断临时线程是否满了,否 创建线程执行任务,是则拒绝处理器执行拒绝处理
d.ScheduledExecutorService - 定时调度执行器服务。很多定时调度框架的底层用的就是这个线程池
3.构建线程池

public class ExecutorServiceDemo {

    public static void main(String[] args) {

        // int corePoolSize - 核心线程数量
        // int maximumPoolSize - 最大线程数量 = 核心线程数 + 临时线程数
        // long keepAliveTime - 临时线程用完之后的存活时间
        // TimeUnit unit - 时间单位
        // BlockingQueue<Runnable> workQueue - 工作队列
        // handler - 拒绝执行处理器 - 如果有明确的拒绝流程,那么可以添加这个参数
        ExecutorService es = new ThreadPoolExecutor(
                5, 
                12, 
                5, TimeUnit.SECONDS, 
                new ArrayBlockingQueue<>(8),
                (r, e) -> {
                    System.out.println("拒绝执行" + r);
                }
        );
        
        for (int i = 0; i < 22; i++) {
            es.submit(new ESThread());
        }
        // 关闭
        es.shutdown();
    }

}

class ESThread implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("start");
            Thread.sleep(3000);
            System.out.println("end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

● Callable
1.概念:Callable是JDK1.5提供一套定义线程的机制
2.启用Callable方式
a.将Callable包装成Runnable来执行且通过泛型来定义返回值类型
在这里插入图片描述
b.通过线程池启用Callable
在这里插入图片描述
3.Runnable和Callable的比较
在这里插入图片描述
六.LOCK–锁
●概述:Lock是JDK1.5提供的一套用于保证线程安全的锁机制, 实际生产中更推荐使用Lock取代sychronized,Lock更加的灵活和精细 。
●Lock锁的分类:
1.ReentrantLock–重入锁
a. 重入锁:当锁资源被释放后,这个锁可以再次被线程占用
b.非重入锁:当锁资源被释放之后,不能被再次使用
2.排他锁中的自旋锁和其他排他锁的区别
a.自旋锁也是排他锁:当线程发现锁资源被占用之后,此时线程不会陷入等待状态,而是会持续判断锁资源是否被占用,直到锁资源被释放。
b.其他的排他锁:线程发现锁资源被占用之后,线程会陷入等待状态,直到锁资源被释放之后,被等待的线程才会醒来然后去抢占锁资源
3.ReadWriteLock-读写锁
a.读锁:允许多个人同时读,不允许写入—共享锁
b:只允许一个人写,不允许读—排他锁
4.公平和非公平策略
a.非公平策略:在资源有限的情况下,线程之间的实际执行次数并不均等
b.公平策略的前提,线程之间并不能直接抢占资源,而是抢占入队顺序,此时每一个线程的实际执行次数是大致相等的。
●其他(似锁非锁)
1.CountDownLatch:闭锁/线程递检索
a.对线程进行计数,在计数归零之前,线程会陷入阻塞;直到计数归零为止,那么才会放开阻塞
b.特点:上一组线程结束开启下一组线程
2.CyclicBarrier:栅栏
a.对线程进行计数,在计数归零之前,线程会陷入阻塞;直到计数归零为止,那么才会放开阻塞
b.特点:所有线程到达同一个点之后再分别继续执行
3.Exchanger: 交换机
a.于交换两个线程之间的信息
b.只能是两个线程
4.Semaphore: 信号量
a.在执行逻辑之前,线程需要先获取信号,当信号被全部获取完那么后来的线程会被阻塞,直到有信号被释放为止。
b.信号量适用于进行限流
七.Atomic操作–原子性操作
●概述:所谓的原子性操作,实际上就是针对属性来提供大量的线程安全的方法,在JDK1.8中采用CAS+volatile机制来保证属性的线程安全。
●volatile
1.简述:volatile是java中的关键字之一,是一种轻量级的线程间的通信机制
2.特征:
a.保证可见性:当共享资源发生变化,其他线程能够立即感知这种变化,并且做出对应的操作。
b.不保证线程原子性:原子性指的是线程执行过程不可分割
c.禁止指定重排:指令重排指的是实际执行顺序和预定义的顺序不一致 。
注意:每一步过程中,发生指令重排的概率不足百万分之一
3.Atomic代码测试

public class AtomicDemo {

    // static int i = 0;
    static AtomicInteger ai = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch cdl = new CountDownLatch(2);
        new Thread(new Add(cdl)).start();
        new Thread(new Add(cdl)).start();

        cdl.await();
        System.out.println(ai);

    }

}

class Add implements Runnable {

    private final CountDownLatch cdl;

    public Add(CountDownLatch cdl) {
        this.cdl = cdl;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            AtomicDemo.ai.getAndIncrement(); // i++
        }
        cdl.countDown();
    }
}

本人个人总结,仅供参考,希望对大家有所帮助,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值