Thread 几种创建方式

162 阅读5分钟

Thread

创建线程的四种方法:
  1. 继承Thread类创建线程类,然后创建线程实例。
  2. 实现Runnable借口创建线程目标类,然后创建线程实例。
  3. 使用Callable和FutureTask创建异步任务,然后创建线程实例。
  4. 通过线程池创建线程

Runnable异步任务的问题:

  1. 不能获取异步执行目标的结果
  2. 不能取消异步执行的任务

解决方法: 使用“可以进行管理的异步任务” 相关类: Future接口和FutureTask 类型

start和run方法的区别:

  1. 用start()来启动线程,实现了真正意义上的启动线程,此时会出现异步执行的结果,即在Thread实例所对应的内核线程,去执行run方法中的业务代码。 start()启动了thread实例的生命周期。
  2. 直接调用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本地变量使用场景

  1. 线程隔离
  2. 跨函数传递数据

为什么需要使用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()时:

  1. 如果线程处于被阻塞状态(例如处于sleep,wait,join等状态),那么线程将立即退出阻塞状态,并抛出一个异常InterruptedException异常。【Java类库中提供的一些可能会发生阻塞的方法都会抛InterruptedException异常,如:BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep】
  2. 如果线程处于正常活动状态,那么会将线程的中断标识设置为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);
    }
    
它会做两步操作:
  1. 返回当前线程的中断状态;
  2. 将当前线程的中断状态设为false;