执行完全分开,方便维护。
60、创建一个线程池
以一个使用较多的
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
为例:
- 其中的 corePoolSize 为线程池的基本大小。
- maximumPoolSize 为线程池最大线程大小。
- keepAliveTime 和 unit 则是线程空闲后的存活时间。
- workQueue 用于存放任务的阻塞队列。
- handler 当队列和最大线程池都满了之后的饱和策略。
61、处理流程
当提交一个任务到线程池时它的执行流程是怎样的呢?
首先第一步会判断核心线程数有没有达到上限,如果没有则创建线程(会获取全局锁),满了则会将任务丢进阻塞队列。
如果队列也满了则需要判断最大线程数是否达到上限,如果没有则创建线程(获取全局锁),如果最大线程数也满了则会根据饱和策略处理。
常用的饱和策略有:
- 直接丢弃任务。
- 调用者线程处理。
- 丢弃队列中的最近任务,执行当前任务。
所以当线程池完成预热之后都是将任务放入队列,接着由工作线程一个个从队列里取出执行。
62、合理配置线程池
线程池并不是配置越大越好,而是要根据任务的熟悉来进行划分: 如果是 CPU 密集型任务应当分配较少的线程,比如 CPU 个数相当的大小。
如果是 IO 密集型任务,由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2 。
当是一个混合型任务,可以将其拆分为 CPU 密集型任务以及 IO 密集型任务,这样来分别配置。
63、synchronize 实现原理
众所周知 Synchronize 关键字是解决并发问题常用解决方案,有以下三种使用方式:
- 同步普通方法,锁的是当前对象。
- 同步静态方法,锁的是当前 Class 对象。
- 同步块,锁的是 {} 中的对象。
实现原理: JVM 是通过进入、退出对象监视器( Monitor )来实现对方法、同步块的同步的。
具体实现是在编译之后在同步方法调用前加入一个 monitor.enter 指令,在退出方法和异常处插入 monitor.exit 的指令。
其本质就是对一个对象监视器( Monitor )进行获取,而这个获取过程具有排他性从而达到了同一时刻只能一个线程访问的目的。
而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit 之后才能尝试继续获取锁。
流程图如下:
synchronize 很多都称之为重量锁,JDK1.6 中对 synchronize 进行了各种优化,为了能减少获取和释放锁带来的消耗引入了偏向锁和轻量锁。
64、轻量锁
当代码进入同步块时,如果同步对象为无锁状态时,当前线程会在栈帧中创建一个锁记录(Lock Record)区域,同时将锁对象的对象头中 Mark Word 拷贝到锁记录中,再尝试使用 CAS 将 Mark Word 更新为指向锁记录的指针。
如果更新成功,当前线程就获得了锁。
如果更新失败 JVM 会先检查锁对象的 Mark Word 是否指向当前线程的锁记录。
如果是则说明当前线程拥有锁对象的锁,可以直接进入同步块。
不是则说明有其他线程抢占了锁,如果存在多个线程同时竞争一把锁,轻量锁就会膨胀为重量锁。
65、解锁
轻量锁的解锁过程也是利用 CAS 来实现的,会尝试锁记录替换回锁对象的 Mark Word 。如果替换成功则说明整个同步操作完成,失败则说明有其他线程尝试获取锁,这时就会唤醒被挂起的线程(此时已经膨胀为重量锁)
轻量锁能提升性能的原因是:
认为大多数锁在整个同步周期都不存在竞争,所以使用 CAS 比使用互斥开销更少。但如果锁竞争激烈,轻量锁就不但有互斥的开销,还有 CAS 的开销,甚至比重量锁更慢。
66、偏向锁
为了进一步的降低获取锁的代价,JDK1.6 之后还引入了偏向锁。
偏向锁的特征是:锁不存在多线程竞争,并且应由一个线程多次获得锁。
当线程访问同步块时,会使用 CAS 将线程 ID 更新到锁对象的 Mark Word 中,如果更新成功则获得偏向锁,并且之后每次进入这个对象锁相关的同步块时都不需要再次获取锁了。
67、释放锁
当有另外一个线程获取这个锁时,持有偏向锁的线程就会释放锁,释放时会等待全局安全点(这一时刻没有字节码运行),接着会暂停拥有偏向锁的线程,根据锁对象目前是否被锁来判定将对象头中的 Mark Word 设置为无锁或者是轻量锁状态。
偏向锁可以提高带有同步却没有竞争的程序性能,但如果程序中大多数锁都存在竞争时,那偏向锁就起不到太大作用。可以使用 -XX:-userBiasedLocking=false 来关闭偏向锁,并默认进入轻量锁。
本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q