java并发
2021-03-25 18:37:47 9 举报
AI智能生成
整理不对地方,请指正
作者其他创作
大纲/内容
并发
线程的并发工具类
CountDownLatch
栅栏、计数器
用于使一系列线程在同一时刻一起执行
先定义出 CountDownLatch 的 容量,在需要等待的地方执行 await() 在需要执行触发的地方执行 countDown() 方法,直到 CountDownLatch 的容量为0 则执行 await() 之后的业务方法
CyclicBarrier
循环屏障
让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
还多了一个回调函数功能
Semaphore
springcloud 断路器 实现 Hystrix
一种线程池
一种信号量
Exchanger
线程之间交换数据
两个线程之间在达到某个同步点的时刻进行数据交换,一般不会用
Callable、Future和FutureTask
FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
CompletableFuture
Fork/Join思路
什么是分而治之
工作窃取
即当前线程的Task已经全被执行完毕,则自动取到其他线程的Task池中取出Task继续执行。
并发容器
ConcurrentHashMap
1.7中HashMap死循环分析
HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry。
HashTable是如何实现线程安全的
HashTable容器使用synchronized来保证单操作线程安全
哈希冲突可解决办法
再散列函数法
链地址法
实现
sizeCtl
-1代表正在初始化
-N 表示有N-1个线程正在进行扩容操作
正数或0代表hash表还没有被初始化
jdk7
数据结构
链表
锁思想
Segment
分段锁
对每个元素的hashCodefont color=\"#c41230\
jdk8
改进
其他并发容器
ConcurrentLinkedQueue(无界非阻塞队列)
基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,LinkedList的并发版本。
使用CAS来进行入队出队
写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
弊端
读和写分别是不同的对象,读不到最新的内容
只能保证最终一致性
适用场景
都多写少的场景
阻塞队列
概念
支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。
场景
常用于生产者/消费者模式
常用队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
读锁和写锁是分开的,将枚举对象包装为Node<E>才可以进行操作,可以不指定队列大小,默认是 Integer.MAX_VALUE
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列
DelayQueue:一个使用优先级队列实现的无界阻塞队列
场景使用
缓存到期
限时支付
SynchronousQueue:一个不存储元素的阻塞队列
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
CAS
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
Disruptor
传统队列问题
利用加锁来保证线程安全问题
存在伪共享问题
更新
使用环型数组结构
使用CAS操作
避免了创建/销毁锁的开销
只有一个角标 sequence 来标识这个环的起点
线程池
为什么要用线程池
降低资源消耗
便于线程管理
提高响应速度
实现原理
判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里
判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务
线程池的创建
核心参数
corePoolSize(线程池基本大小)
当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建
没有任务需要执行的时候,线程池的大小不一定是corePoolSize
maximumPoolSize 最大线程数
队列满了会启用 最大线程数来创建新的线程
ThreadFactory
自定义线程的名字格式
runnableTaskQueue(任务队列)
保存等待执行任务的队列
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
keepAliveTime
线程保持活动时间
TimeUnit
RejectedExecutionHandler(饱和策略)
AbortPolicy
抛出异常(默认策略)
CallerRunsPolicy
使用调用者线程来执行任务
DiscardOldestPolicy
丢弃队列中最靠前的任务,并执行当前任务
DiscardPolicy
直接丢弃任务,采用不理睬的对策
线程池提交
execute()
submit()
有返回值提交
线程池关闭
shutdown
shutdownNow
线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程
Executor框架
ThreadPoolExecutor框架
FixedThreadPool
固定线程大小
SingleThreadExecutor
CachedThreadPool
适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器
ScheduledThreadPoolExecutor
适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景。
SingleThreadScheduledExecutor
适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景。
WorkStealingPool
利用所有运行的处理器数目来创建一个工作窃取的线程池,使用forkjoin实现
FutureTask
基础概念
cpu时间片轮转机制
进程和线程
并行和并发
性能指标
吞吐量
单位时间内能处理的请求数量。吞吐量越高,说明性能越好
延迟
发出请求到收到响应的时间。延迟越小,说明性能越好
能同时处理的请求数量,一般来说随着并发量的增加、延迟也会增加
线程所需要满足的特性
原子性
可见性
有序性
font color=\"#c41230\
死锁
一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象
必备的四个条件
互斥,共享资源 X 和 Y 只能被一个线程占用
占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X
破坏: 一个线程直接申请所有所需的共享资源
不可抢占,其他线程不能强行抢占线程 T1 占有的资源
循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待
破坏:按序申请资源来预防
活锁
任务没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程
让线程随机休眠一段时间
饥饿锁
一个线程因为 CPU 时间全部被其他线程抢占而得不到 CPU 运行时间,导致线程无法执行
优先级线程吞噬所有的低优先级线程的 CPU 时间
其他线程总是能在它之前持续地对该同步块进行访问,线程被永久堵塞在一个等待进入同步块
其他线程总是抢先被持续地获得唤醒,线程一直在等待被唤醒
java线程基础
启动和终止线程
实现线程方式
单继承的局限性
基于Runnable接口实现
基于Callable接口实现
可以利用 FutureTask<V> 进行包装,然后利用 future.get() 拿到返回值
如何安全的中止线程
建议使用interrupt操作
不用stop原因
终结一个线程时不会保证线程的资源正常释放,通常是没有给予线程完成资源释放工作的机会,因此会导致程序可能工作在不确定状态下
线程再认识
线程状态
初始状态
实现线程,new操作就会变为初始状态
运行状态
线程的start方法
阻塞状态
有时限等待
无时限等待
终止状态
线程状态之间转换
runnable到blocked的状态
synchronized锁
runnable到waiting的状态
Object.wait方法
Thread.join()
LockSupport.park()(与wait和notify功能类似)
runnable到timed_waiting状态
Thread.sleep()方法
waiting状态那些方法带时间参数
runnable到termanited状态
run方法执行完
stop方法()
不建议使用
该方法会font color=\"#c41230\
中断方法 --interrupt
线程间的共享
synchronized
用法
给对象加锁
当前实例加锁
类锁
对某些静态方法进行加锁
volatile
使用场景
线程之间需要加标志位实现变化操作
禁止指令重排序
读多写少的场景
ThreadLocal
shiro框架中使用
分布式事务框架 seata 中 ,进行保存 Xid 的时候
ThreadLocalMap
内存泄露分析
线程间协作
等待通知机制
wait/notify/notifyall
必须配合synchronized关键字使用
join方法
A.join(b),A线程执行完了才会执行B线程
调用yield() 、sleep()、\u000Bwait()、notify()等方法对锁有何影响?
yield() sleep() 不会去释放锁
wait() 会释放锁
notify() 对锁没有影响,但是线程只有在syn同步代码执行完后才会自然而然的释放锁,所以notify()系列方法一般都是syn同步代码的最后一行。
原子操作CAS
java中的原子类都是基于CAS实现
CAS原理
循环 and 交换
问题
ABA问题
只能保证一个变量的共享操作
原子操作类使用
基本类型
数组
对象引用
乐观锁类似,加个版本戳
Java8新增
LongAdder
LongAccumulator
显示锁和AQS
显示锁
Lock接口
与synchronized的区别
Lock可以手动释放锁,synchronized关键字不能手动释放,异常释放或者正常释放
都是可重入锁
ReentrantLock类(独享锁)
可重入锁
一个线程可以多次获取该锁
公平锁/非公平锁
ReadLock接口
读锁(共享锁)
读锁不能升级为写锁
写锁(排他锁)
写锁可以降级为读锁
jdk8中的StampLock
读写锁的升级版
不支持可重入锁
写锁
悲观读锁
乐观读
它允许在有读锁情况下,允许一个font color=\"#c41230\
Condition接口
用Lock和Condition实现等待通知
AQS(AbstractQueuedSynchronizer)
学习必要
基本上JUC包底下大部分的锁都是基于AQS设计实现的
核心思想
如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制
构建锁或者其他同步组件的基础框架使用了一个volatile int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作
CLH队列
一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系
一种基于链表的可扩展、高性能、公平的自旋锁,申请线程仅仅在本地变量上自旋,它不断轮询前驱的状态,假设发现前驱释放了锁就结束自旋
CAS方法设置尾节点
设计模式
模板方法
0 条评论
回复 删除
下一页