线程与线程池

线程状态 坦率地说,线程有五种状态:NEW、RUNNABLE、RUNNING、BLOCKED 和 TERMINATED。

线程的实现方法及区别 线程的实现方式主要有两种:Runnable和Thread。
Runnable更加灵活,适合多线程场景。
线程有自己的执行方法,但耦合度很高。
Callable和FutureTask一起使用来获取执行结果。
我们去年运行的一个项目利用 Runnable 的雪崩效应导致整个系统瘫痪。
说实话,当时确实挺尴尬的。

start() 和 run() 之间的区别 start() 启动一个线程,run() 执行代码。
直接调用run()相当于单线程执行。
用技术术语来说,这称为线程池效应,意味着没有并发。
还有一点是start()创建执行线程而run()直接使用主线程。

Thread.sleep() 和 Thread.yield() 睡觉就是阻拦,让步就是礼让。
sleep让出CPU,等待3 秒等,然后加锁。
Yield 意味着当前线程返回到就绪状态(如“嘿,我会把它给你”)。
一开始我觉得收益率很好,后来发现高并发下,获取资源的优先级是很棘手的。

等待和睡眠的区别 1 )wait是一个对象方法,sleep是一个线程静态方法。
2 )wait必须同步使用。
如果没有,就会报错。
3 )wait是对象监听器,notify随机唤醒,sleep不释放锁。
4 )wait放弃资源,sleep不放弃资源。
两者都屈服于CPU。

用户线程和守护线程 守护线程使用thread.setDaemon(true)。
必须在启动之前进行配置。
例如,当JVM关闭时,用户线程结束运行,守护线程直接运行。
去年我们的项目忘记设置它。
结果守护线程卡住了,系统卡了很长时间。

线程调用方法 1 )start()方法直接启动,适合外部显式调用。
2 )执行者更安全。
例如,cachedThreadPool自动管理线程。
执行器在构造期间不会导致 start() 并发问题。

线程停止方法 1 )interrupt()是最优雅的。
2 ) 使用紧急出口标志。
3 )stop()已被废弃,数据可能不一致。

Executor调用线程模式 缓存线程池每个任务有一个线程。
fixThreadPool 共享固定数量的线程。
例如cachedThreadPool适合短任务,fixedThreadPool适合CPU密集型任务。

并发依赖问题 由于关闭顺序不确定,任务之间不能存在直接依赖关系。
比如使用 volatile 变量或者锁来解决。

如何锁定 同步并锁定。
锁更细粒度,例如你可以尝试获取锁。

线程池状态 5 种类型:运行、关机、停止、整理、退出。

关机和停止的区别 都不接受新线程,但shutdown完成处理旧任务,并暴力停止中断。

ThreadPoolExecutor参数 corePoolSize 是核心线程数,maximumPoolSize 是最大线程数。
workQueue 共有三种类型。
cachedThreadPool 使用 SynchronousQueue。
这是公平的,但会增加等待时间。

线程循环调用 例如,使用 ScheduledExecutor 每 5 秒运行一次,初始化后延迟 1 秒。

提醒:使用ThreadPool时,不要忘记调整keepAliveTime。
否则,如果线程不释放,内存就会爆炸。

线程池的七个参数(加上四种常见的工作队列和拒绝策略)