Thread
创建线程的四种方法:
- 继承Thread类创建线程类,然后创建线程实例。
- 实现Runnable借口创建线程目标类,然后创建线程实例。
- 使用Callable和FutureTask创建异步任务,然后创建线程实例。
- 通过线程池创建线程
Runnable异步任务的问题:
- 不能获取异步执行目标的结果
- 不能取消异步执行的任务
解决方法: 使用“可以进行管理的异步任务” 相关类: Future接口和FutureTask 类型
start和run方法的区别:
- 用start()来启动线程,实现了真正意义上的启动线程,此时会出现异步执行的结果,即在Thread实例所对应的内核线程,去执行run方法中的业务代码。 start()启动了thread实例的生命周期。
- 直接调用Thread实例的run() ,相当于当前线程直接执行一次其业务逻辑代码,是同步执行,就不是异步执行了,不会达到使用线程的意义。 run()没有启动thread实例的生命周期。就是一个普通方法的同步调用
线程数配置方案
IO密集型任务创建线程池:CPU核心数2-1 CPU密集型任务创建线程池:CPU核心数 混合型任务创建线程池:最佳线程数 = ((线程等待时间 + 线程CPU时间) / 线程CPU时间) CPU核心 最佳线程数 = (线程等待时间与线程CPU时间之比 + 1) * CPU核数 例:比如在web服务器处理HTTP请求时,假设平均线程CPU运行时间为100ms,而线程等待时间(比如包括DB操作/RPC操作/缓存操作等)为900ms,如果CPU核心数为8,那么根据上面这个公司,估算如下:
(900ms + 100ms) / 100ms * 8 = 10*8 = 80
经过计算,以上案例中需要的线程数为80
ThreadLocal
无锁编程的实现方案 “线程本地变量” 可以看出专属于线程的变量,不受其它线程干扰,保存着线程的专属数据。当线程结束后,每个线程所拥有的那一个本地值也会被释放。在多线程并发操作“线程本地变量”时候,线程各自操作的是自己的本地值,从而规避了线程安全的问题。
ThreadLocal本地变量使用场景
- 线程隔离
- 跨函数传递数据
为什么需要使用private static final 修饰ThreadLocal 变量?
使用final进行加强修饰的原因:以防止其在使用过程中发生动态变更;
static final 修饰ThreadLocal对象
这使得Thread实例内部的ThreadLocalMap中Entry的key 在 Thread 实例的生命周期内将始终保证为非null,从而导致Key所在的Entry不会被自动清空,这就会导致Entry中的value指向的对象一直存在强引用 value 指向的对象在线程生命周期内不会被释放, 最终导致内存泄露。
所以: 使用完后必须使用remove() 进行手动释放。
ThreadLocalMap 中的Entry 的key为弱引用;
概念
阻塞表示线程的一种状态,在这种状态下,线程是不占用CPU的(也就是说,不执行你写的命令代码的),更进一步来说,也就是你的代码在执行过程中,在某个地方暂停了。
首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」,具体到底中断还是继续运行,应该由被通知的线程自己处理。
具体来说,当一个线程,调用interrupte()时:
- 如果线程处于被阻塞状态(例如处于sleep,wait,join等状态),那么线程将立即退出阻塞状态,并抛出一个异常InterruptedException异常。【Java类库中提供的一些可能会发生阻塞的方法都会抛InterruptedException异常,如:BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep】
- 如果线程处于正常活动状态,那么会将线程的中断标识设置为true。被设置中断标志的线程将继续正常运行,不受影响。
中断
在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制-------中断。
- 中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的interrupted方法,该方法也仅仅是将线程对象的中断标识设成true;接着你需要自己写代码不断地检测当前线程的标识位;如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。
- 每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;
- 通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。
中断的相关方法
- public void interrupt() 将调用者线程的中断状态设为true。
- public boolean isInterrupted() 判断调用者线程的中断状态。
- public static boolean interrupted 只能通过Thread.interrupted()调用。 清除执行此方法的线程的中断标识 ,
-
public static boolean interrupted() {return currentThread().isInterrupted(true); }
它会做两步操作:
- 返回当前线程的中断状态;
- 将当前线程的中断状态设为false;