JAVA 多线程与锁

2020-07-17  本文已影响0人  三石_5f43

JAVA 多线程与锁

线程与线程池

线程安全可能出现的场景

多线程性能问题

线程调度

线程协作

每个任务都创建一个线程带来的问题

线程池解决线程资源过多的思路

线程池的优点

线程池参数

参数名 含义
corePoolSize 核心线程数
maxPoolSize 最大可创建线程数
KeepAliveTime 空闲线程的存活时间
ThreadFactory 线程工厂,定义创建新线程<br />的规则
workQueue 用于存放任务的队列
Handler 按任务拒绝策略 处理被拒绝任务

线程池任务调度流程图

soket Program-线程池任务处理.png

线程池的特点

线程池拒绝任务的时机

线程池的拒绝策略

常见的6种线程池

线程池 corePoolSize maxPoolSize keepAliveTime
FixedThreadPool 构造函数传入 同corePoolSize 0
CacheThreadPool 0 Integer.MAX_VALUE 60s
ScheduledThreadPool 构造函数传入 Integer.MAX_VALUE 0
SingleThreadExecutor 1 1 0
SingleThreadScheduledExecutor 1 Integer.MAX_VALUE 0
FixedThreadPool

核心线程数(corePoolSzie) 和最大线程数(maxPoolSize) 一样, 可以看做是固定线程数的线程池, 特点是线程池中的线程从0 开始增加,到corePoolSize 线程数上限,就不再增加。

CacheThreadPool

可以称为可缓存线程池, 它的线程数是几乎可说是不设上限,(Integer.MAX_VALUE 2^31-1),它的任务队列是SynchronousQueue 队列容量为0 , 不存储任务,只负责任务的中转与传递,效率比较高。
当提交一个新任务时线程池会判断是否存在空闲线程,如果有空闲线程就直接分配任务给线程去执行,没有则新建线程去执行任务。

ScheduledThreadPool

支持定时和周期性的执行任务。

ScheduledExecutorService service = Executors.newScheduledThreadPool(10); 
#1 每隔10s 钟执行一次任务。
service.schedule(new Task(), 10, TimeUnit.SECONDS); 
#2 延迟10s 执行第一次任务,之后(从任务开始执行时间计时) 每延迟10s 执行一次任务。
service.scheduleAtFixedRate(new Task(), 10, 10, TimeUnit.SECONDS); 
#3 延迟10s 执行第一次任务,之后(从任务结束时间开始计时)每延迟10s 执行一次任务。
service.scheduleWithFixedDelay(new Task(), 10, 10, TimeUnit.SECONDS);
SingleThreadExecutor

只存在一个线程去执行任务,如果线程执行任务过程中发生异常,线程池会创建新的线程去执行后续的任务。适合要求任务按提交顺序执行的场景。

SingleThreadScheduledExecutor

与 ThreadScheduledExecutor 类似,只是它的核心线程数设为了1,只有一个线程去执行任务。

ForkJoinPool

适用于递归场景,例如树的遍历。。

与上述线程池最大的不同点在于

合适的线程数量

CPU 密集型

加密、解密、压缩、计算等一系列需要大量消耗CPU 资源的任务, 这样的任务最大线程数为 CPU core*(1~2)。 因为计算任务是比较繁重的,会占用大量的CPU 资源, 申请过多线程容易造成线程的上下文切换,甚至会导致性能的下降。

IO 密集型

数据库数据读写、 文件内容读写、网络通信等任务,这种任务的特点是并不会特别消耗CPU 资源,但是IO 操作耗时,会占用比较多的时间。 线程数=CPU core * (1+ 平均等待时间/平均工作时间)。

线程池的关闭

shutdown()
shutdownNow()

执行shutdownNow() 方法后,会执行以下步骤

isShutdown()

检测线程池是否已经开始了关闭流程(执行了shutdown() shutdownNow()), Boolean 类型,返回为true 也只是表明线程池开始了关闭工作。

isTerminated()

检测线程池是否已经终结关闭掉,Boolean 类型,返回为true 意味者线程池中任务队列里的任务都已经执行完毕,线程池被关闭。

awaitTermination()

用来判断线程池状态,在等待时间截止可能三种情况产生。

多线程的线程复用原理

常见的各种锁

锁的7 大分类

偏向锁/轻量级锁/重量级锁

对于synchronized 关键字加monitor 锁的对象,在对象头中标明锁的状态。

soket Program-锁升级.jpg

​ 偏向锁的性能最好,此时没有出现多线程的竞争,轻量级锁利用自旋+CAS 操作避免了重量级带来的线程阻塞和唤醒,性能中等,重量级锁则会把获取不到线程的锁阻塞,性能最差。

可重入锁/不可重入锁
共享锁/排他锁
公平锁/非公品锁

如果线程线程在尝试获取锁的时候获取不到锁,就会陷入阻塞等待,开始排队,在等待队列里等待长的优先获得锁,先到先得,而非公平锁在一定情况下会忽略掉正在排队的线程,发生插队现象。

悲观锁/乐观锁
自旋锁/非自旋锁
soket Program-自旋锁非自旋锁.jpg
可中断锁/不可中断锁

synchronized vs Lock

相同点
不同点
如何选择

JVM 对锁的优化

自适应自旋锁
锁粗化
锁消除
偏向锁/轻量级锁/重量级锁

参见上文

上一篇下一篇

猜你喜欢

热点阅读