并发理解 part1

线程池

线程池的好处
在这里插入图片描述
线程池适用的场合:
在这里插入图片描述
在这里插入图片描述

增减线程的时机:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
增减线程的特点:
在这里插入图片描述
线程回收:
在这里插入图片描述
在这里插入图片描述
通常情况下,不用自己写线程工厂,源码其实就是new Thead 传入不同参数即可

在这里插入图片描述
使用情况,以及优缺点

手动创建线程池还是自动创建线程池:
在这里插入图片描述
在这里插入图片描述
SynchronousQueue其实内部不存储,不用队列中转,直接去执行
在这里插入图片描述
这个了解就可以:
在这里插入图片描述
如何停止线程池
开始结束
开始结束查看
真正结束查看
在这里插入图片描述
在这里插入图片描述
线程等待一段时间,返回结果,这个只是个检测方法
当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行

在这里插入图片描述
在这里插入图片描述
对于正在运行的线程,设置interupt中断状态位,队列中的直接返回,返回一个列表(中断线程)List List.,可以用新的线程池继续执行。

拒绝策略

拒绝时机:
在这里插入图片描述
在这里插入图片描述

CallerRunsPolicy 进行负反馈,线程池会腾出空闲,给一些缓冲

高级用法:可暂停的线程池
用的时候再看
在这里插入图片描述
在这里插入图片描述

原码分析

在这里插入图片描述
管理器就是用来创建,删除线程的
工作线程就是用来执行任务的线程(Work对象)
在这里插入图片描述

在这里插入图片描述
Executor只有一个方法
在这里插入图片描述
ExecutorService extends Executor
Executors 创建线程静态工厂

线程池是如何实现任务复用的原理
在这里插入图片描述
这个线程不是普通的线程,在这个线程的run 方法里面 会不停检测有没有新的任务进来,有的话就会调用这个新的任务的run方法。把这一些列的任务串联起来执行

线程池 让我们 不同的 从队列中 取任务出来并执行

executor源码分析:
在这里插入图片描述
在这里插入图片描述
Worker就是工作线程,再runworker之中直接调用task的run方法,task是个runnable实现,getTask()就是从阻塞队列中取出来

线程池使用注意点
在这里插入图片描述
线程泄露就是线程结束不了,一般是写的业务逻辑有问题

ThreadLocal

使用场景:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ThreadLocal的两个作用:
在这里插入图片描述
如何向ThreadLocal中存放数据,怎么选择
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ThreadLocal的好处:
在这里插入图片描述
在这里插入图片描述
TheadLocal原理:
ThreadLocal就是个工具类
在这里插入图片描述
ThreadLocalMap是Thread的成员变量
在这里插入图片描述
一个线程拥有多个threadLocal

initialValue 源码解析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Get方法源码解析:
在这里插入图片描述
this就是TheadLocal对象
在这里插入图片描述在这里插入图片描述
Set方法源码解析:
在这里插入图片描述
在这里插入图片描述
remove 源码解析:
在这里插入图片描述

总结:
在这里插入图片描述
不是拉链方法:
在这里插入图片描述
内存泄漏问题:
在这里插入图片描述

构造函数不一样,利用了弱引用了,在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
空指针异常:
小心包装类型,要有拆箱,装箱,在没有set的情况下,get就会返回null
在这里插入图片描述
共享对象问题:
在这里插入图片描述

Spring 中ThreadLocal的应用:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
保存时间上下文

在这里插入图片描述
保存请求信息,里面有请求参数,请求头
在这里插入图片描述

锁加强

为什么sync不够用?
在这里插入图片描述
所以Lock接口就出来了:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

可见性保证

happens-before原则,JMM靠这个解决可见性问题

在这里插入图片描述在这里插入图片描述

锁的分类

这些分类会导致角度不同,分类角度不同
在这里插入图片描述
乐观锁与悲观锁
乐观锁 为什么产生:
在这里插入图片描述
优先级低的线程获取锁,并且不能释放,高优先级就悲剧了

在这里插入图片描述
git就是乐观锁
在这里插入图片描述
git就不适合悲观锁,公司会倒闭
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

重入锁与不可重入锁:
电影院买票就是需要锁的场景,锁定三分钟
在这里插入图片描述
其他方法介绍:
在这里插入图片描述
公平锁与非公平锁:
有非公平锁,是避免有空档期
A用完,去唤醒挂起的B,这时候C来了并且会很快结束,C直接用吧插队,用完,B也唤醒了,就接着B,这种差别会导致吞吐量的提升,唤醒过程开销是比较大的。
在这里插入图片描述
红框先判断有没有队列里的内容在这里插入图片描述

共享锁于排它锁:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
另一种理解方式:
在这里插入图片描述
在这里插入图片描述

读锁和写锁的交互方式
就比如男女共同上厕所,男生可以插队嘛?

在这里插入图片描述
循允许降级不许升级,不允许读锁插队
在这里插入图片描述
公平方式源码:在这里插入图片描述

写锁,永远永远不用看人脸色,直接插队在这里插入图片描述
看第一个是不是想获取写锁的线程。
在这里插入图片描述
这些方法都在tryAcquired方法里面被调用。

比如有些任务开始写一些日志,后面开始读取一些文件,这样在持有写锁时候,完成读
在这里插入图片描述

先获取写锁,再获取读锁,在持有的时候获取读锁,降级的好处可以提高效率

升级锁会导致线程阻塞? 就是因为读锁和写锁不能同时存在。
在这里插入图片描述

自旋锁与阻塞锁

怎么选?
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

Atomic包下的基本都是自旋锁实现的,基本都是CAS
在这里插入图片描述

自己写一个简单的自旋锁:
需要一个原子操作,利用CAS将值换上去
在这里插入图片描述
使用场景:
在这里插入图片描述
可中断锁与不可中断锁:
在这里插入图片描述

Java对虚拟机对锁的优化:
JRET编译器会动态检测
在这里插入图片描述
写代码时候如何提高锁并发性能:
在这里插入图片描述
在这里插入图片描述
写日志框架,打印日志需要加锁,用消息队列,减少了锁的次数

Atomic包加强

在这里插入图片描述
在这里插入图片描述
6类原子类:
在这里插入图片描述
在这里插入图片描述
a++ 本身分为三步操作

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在类不是自己写的时候,需要这种操作 or 有些情况下才需要并发,其余时间不需要
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
内部是反射原理,所以可见范围是public的,static修饰会爆出异常

Java8 才引入 Adder 累加器
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sum源码

在这里插入图片描述
sum过程中没有锁,cell有些变化,但求和已经结束了,求和不是很准确
在这里插入图片描述
Accumulator的使用在这里插入图片描述

CAS

就是完成了不能被打断的数据交换操作
在这里插入图片描述

A是我之前读取过来的,B是我算出来的。
在这里插入图片描述

在这里插入图片描述
最终是要使用CPU的特殊指令的保证原子操作
在这里插入图片描述
在这里插入图片描述
应用场景
1 乐观锁
2 并发容器有很多CAS 体现,比如ConcurrentHashMap
3 原子类

原子类是如何实现CAS源码分析
在这里插入图片描述
在这里插入图片描述
静态代码块,拿到了value的地址

在这里插入图片描述
这个是unsafe类
在这里插入图片描述
compareANdSwapeInt是个native方法

Unsafe 类在这里插入图片描述

在这里插入图片描述
所以直接操作的是地址,真正的实现是一个cpp
在这里插入图片描述在这里插入图片描述

CAS的缺点:

在这里插入图片描述

不变性实现

在这里插入图片描述

不可变性是并发编程中,重要的环节
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在编译期间 就知道准确值了,而且永远不会变化,所以会当做编译时候的常量来使用,就直接使用常量,不用再运行时候再确定,
c可以确定为wukong2 那么就不会再建立一个常量 wukong2,就指向同一个地址

编译器不会提前知道 d的值,所以e的值在运行时候才能确定,所以e会在堆上生成wukong2
就看在生成字符串的时候,能不能确定值,还是运算时候确定
在这里插入图片描述

并发 集合加强

这三个重点了解
在这里插入图片描述在这里插入图片描述
这个不常用
在这里插入图片描述

Map对应关系:在这里插入图片描述
HashMap 死循环,CPU100% 问题
在这里插入图片描述
核心原因,多个线程同时扩容的时候会导致链表的死循环,你指向我,我指向你 ,考的话,就是看你背没背到题。
在这里插入图片描述
hashMap 作者,他们能知道吗,他们会同样给你一个这个建议
在这里插入图片描述
jdk 1.7 ConcurentHashMap

在这里插入图片描述
在这里插入图片描述

jdk1.8 ConcurrentHashMap 实现和分析

在这里插入图片描述
完全借鉴了1.8HashMap的思想 ,每个绿点是一个node

1.7 源码分析 put get
知道结构图就可以了
在这里插入图片描述

1.8 源码分析put get
put 参考之前笔记
在这里插入图片描述

get方法
在这里插入图片描述
在这里插入图片描述
java8 每个节点都是独立的
java7是由segment
在这里插入图片描述

为什么选择8转为红黑树?
链表节点的存储空间,小于红黑树的存储节点
源码里面有 注解里面,泊松分布概率计算,8次之后发送碰撞概率极小,为了有更好的效率,就变为红黑树
如果有超过8次,就说明哈西算法有问题。毕竟概率千万份之一,实际过程中,基本不会树化
在这里插入图片描述
为什么COncurrentHashMap错误使用,就会是线程不安全?
只保证了 get put 是线程安全,但是运算不是线程安全的。 本身组合操作是不安全的。 只保证同时put 内部数据不错乱
在这里插入图片描述
解决方式就是 sync 保护起来
要么就是 使用replace cas 方式,进行组合操作,取出来换回去
在这里插入图片描述
putIfAbsent 表示同样的原子组合操作
在这里插入图片描述
手写红黑树,很没有意义

CopyOnWriteArrayList
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
迭代时候会数据过期
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
初始化方法:
在这里插入图片描述
add方法:

在这里插入图片描述
get方法,没有什么特别的动作
在这里插入图片描述

并发队列
只列出最主要的队列,掌握这些就够了
在这里插入图片描述
ConcurrentLinkedQueue 非阻塞队列
Deque 是双端队列,除了头尾都可以操作,其余与Queue一样

阻塞队列看下 有无边界
在这里插入图片描述

三组方法
第一组阻塞
第二组抛出异常
第三组,返回false
在这里插入图片描述
在这里插入图片描述
take put 源码分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
put方法源码分析,里面的count是原子操作。在这里插入图片描述
在这里插入图片描述

prioritBlockingQueue,无界的原因是 可以扩容

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因为无线可以开线程,所以就不存储。
在这里插入图片描述
在这里插入图片描述

如何选择适合自己的队列?

在这里插入图片描述
吞吐量就看锁的力度

AQS

在这里插入图片描述
HR 就是AQS
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

比如AQS里面 这种方法 使用unsafe操作在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
head是拿到锁的线程,后面的是等待拿锁的线程
在这里插入图片描述
这个获取方法,和status变量有关在这里插入图片描述
同样依赖于status,但释放方法不会让线程阻塞
在这里插入图片描述

协作工具类需要自己去实现获取or释放等重要方法
在这里插入图片描述
CountDownLatch 源码分析
在这里插入图片描述
在这里插入图片描述

构造方法
在这里插入图片描述
在这里插入图片描述
这里面就设置了 AQS里面的statue

在这里插入图片描述
在这里插入图片描述
里面其实就是返回AQS里面的status

await方法就是等待,直到倒数结束
在这里插入图片描述
在这里插入图片描述
doAcquireSharedInterruptibly 这个方法就是让当前线程进入等待队列并且会让他阻塞,大于等于0就不需要等待了,直接放行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

addWaiter 会把一个个线程包装成Node节点
parkANdCHeckInteruot 就是让线程阻塞,调用了LockSupport.park方法进行阻塞,其实就是调用了unsafe方法的unpark方法(native方法)进行阻塞

在这里插入图片描述
在这里插入图片描述
countDown方法
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
做自旋,主要作用就是status减少1, return nextc==0 说明这个是最关键的倒数,是最后一次倒数,就调用了doReleaseShared()将之前阻塞的方法全部唤醒,就是唤醒队列中的其他线程。
在这里插入图片描述

Semaphore 源码分析
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个tryAcquireShared 根据公平核非公平有两种实现,小于0进入排队等待,大于0就代表这次获取成功
先看非公平情况
在这里插入图片描述

在这里插入图片描述
这个方法不断自选获取许可证的数量 ,所以叫做available,acquires 代表传入的值,代表想获取几个许可证
小于0成立,直接返回remaining ,返回负数,所以就会进行等待队列
如果大于等于0,说明获取许可成功,就自旋更新许可证

在这里插入图片描述
ReentranLock

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
先判断是否是持有锁的线程,
判断冲入次数,c等于0 free状态,将锁的线程持有者,设置为null
如果 tryRelease方法返回的是true,代表锁已经完全释放,则会从队列中唤醒线程去获取锁unparkSuccessor就是去唤醒

在这里插入图片描述
在这里插入图片描述
根据公平于不公平有两种实现
公平情况下:
在这里插入图片描述
线程独有,设置锁的拥有者,如果不成功调用acquire方法
在这里插入图片描述
在这里插入图片描述
看是否是锁的持有者重入情况,如果是则 setState,return true
要么锁已经被其他线程持有 获取不成功 return false,就包装node 添加入队列。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯智能台灯

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值