线程类型
- 守护线程
- 服务于其他线程的线程
- 随程序关闭而关闭,关闭时停止所有后台线程
- 比如垃圾回收线程
- 用户线程
- 用户自定义的线程
线程状态(生命周期)
初始状态(NEW)
- 实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
就绪状态(RUNNABLE之READY)
- 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
- 调用线程的start()方法,此线程进入就绪状态。
- 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
- 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
- 锁池里的线程拿到对象锁后,进入就绪状态。
运行中状态(RUNNABLE之RUNNING)
线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。
阻塞状态(BLOCKED)
- 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
- 等待获取锁的状态
等待状态(WAITING)
- 处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
- 运行状态 –> wait() –> 等待状态 –> notify()/notifyAll() –> 就绪状态
计时等待(TIMED_WAITING)
- 处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
- 运行状态 –> sleep()/wait(1000) –> 计时等待 –> 时间结束回到就绪状态 –> 调度程序选中进入运行状态
终止状态(TERMINATED)
- 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
- 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
线程池
基本参数
- 核心线程数(Core Pool Size):表示核心线程的数量。所谓核心线程,是在"大多数情况下(未开启allowCoreThreadTimeOut参数)"都不会被销毁的线程(即使它们一直处于空闲状态);
- 最大线程数(Max Pool Size):当任务堆积,且池中没有空闲的核心线程来处理任务时,线程池会创建一些临时的线程来处理堆积的任务。这些临时的线程会在空闲一定时间后被销毁掉, 最大的临时线程数量为MaxPoolSize - CorePoolSize;
- 存活时间(Keep Alive Time):分为具体的数值和单位,如60s。表示临时线程空闲存活时间(在开启allowCoreThreadTimeOut参数后也表示核心线程的空闲存活时间)。 当临时线程空闲时间超过该值之后,就会被销毁掉;
- 工作队列(Work Queue):存放挤压的任务;
- threadFactory(线程工厂)不能为空,默认为DefaultThreadFactory类。
- 拒绝策略(Reject Handler):当线程池的线程数量已经达到上限(Max Pool Size),全部都处于非空闲的状态,且工作队列已满无法再堆积任务时, 会按照预先设定的方式拒绝新的任务;默认策略为ThreadPoolExecutor.AbortPolicy。
handler(线程饱和策略):当线程池和队列都满了,则表明该线程池已达饱和状态。
- ThreadPoolExecutor.AbortPolicy:处理程序遭到拒绝,则直接抛出运行时异常 RejectedExecutionException。(默认策略)
- ThreadPoolExecutor.CallerRunsPolicy:调用者所在线程来运行该任务,此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
- ThreadPoolExecutor.DiscardPolicy:无法执行的任务将被删除。
- ThreadPoolExecutor.DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重新尝试执行任务(如果再次失败,则重复此过程)。
多线程三大特性
原子性:即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值*。
有序性:即程序执行的顺序按照代码的先后顺序执行。
- 使用ThreadLocal保证每个线程拿到的对象都是独一份。
- AtomicReference、AtomicLong、AtomicInteger等等保证多线程原子性。(
java.util.concurrent.atomic工具包)
1 | package org.example.thread; |
1 | // 线程A |
过程分析:在执行的时候,可能是线程A先判断,然后B判断,然后B插入,然后A插入,结果B显示插入成功,但是最后拿到的值确实A的值。
解决方法:可以通过putIfAbsent、compute、computeIfAbsent、conputeIfPresent、merge等API进行。
Thread
方法
- Thread.currentThread().isInterrupted()
- 判断是否中断
- Thread.interrupted()
- 判断是否中断,并清除当前中断状态
- stop()方法
- 不推荐使用
- 调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
- 调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
- Thread.currentThread().interrupt();
- 中断线程,并不会立即停止所有操作,只是打一个停止的标记
优雅停止线程
- 通过抛出异常停止
1 | public class ThreadInterrupt { |
- 通过线程sleep的情况下调用interrupt()方法停止
1 | public class SleepThreadInterrupt { |
Leave a comment