Java之线程创建和线程池

线程创建

Java中有三种方式创建线程,分别为继承Thread类,实现Runnable接口和实现Callable接口。

继承Thread

继承Thread类比较简单,就是定义一个类,这个类继承了Thread类,重新定义了Thread类的run方法。这种方式是最直观,在需要创建线程时,直接创建一个继承类的对象即可。但是这种方法在多线程共享资源上没有那么方便,依赖关系会比较复杂。

Runnable接口

实现Runnable接口的方式就是定义一个类,这个类定义了Runnable接口的run方法。这种方式和继承Thread类方式最大的优势就是,不同线程对象之间可以共享一个实现了Runnable接口的对象,所以在线程共享资源和通信上会比继承Tread类的方法方便,依赖关系也会更简单。

Callable接口

实现Callable接口需要和FutureTask类合作才能实现多线程,这种创建线程的方式可以返回每个线程的工作结果,在需要线程的运行结果的场景中非常有用。FutureTask类实现了Runnable接口和Future接口,Future接口封装了查询和设置线程运行结果和线程状态的方法,不仅方便对线程的控制还能提供为主线程捕获子线程异常信息的功能。FutureTask的构造函数中可以传入实现Callable接口的对象,因为实现了Runnable接口,所以可以被用于创建线程;因为实现了Future接口,所以可以被用于控制线程和查询线程运行结果。可以这么理解,实现Callable接口的对象封装了一个运行任务,这个运行任务的运行结果可以通过Callable接口的call方法获得;实现Future接口的对象封装了一个控制任务,这个控制任务可以通过Future接口的各种方法实现;实现Runnable接口的对象可以用于创建线程。三者一起作用实现主线程控制多线程运行状态,获得多线程运行结果,捕获多线程运行异常的场景。

线程状态

在Java中,线程有6种状态,分别是新建、可运行、阻塞、等待、时限等待、终止。新建状态是指线程对象被new出来了但是还没有执行start方法;可运行状态包括就绪和运行两种状态,就绪状态表示线程在等待CPU的时间片;阻塞状态表示线程阻塞于锁,阻塞于同步代码块;等待状态表示线程在等待其他线程的信号与动作;时限等待表示线程在等待其他线程的信号与动作,一段时间没有收到特定信号与动作则放弃等待;终止状态表示线程运行结束。

线程池

工作原理

Java中使用Executors类创建线程池对象,线程池对象可以帮助我们创建与重用线程,从而更好地管理与使用线程,避免频繁创建与销毁线程所造成的不必要开销以及避免多线程造成的空间资源占用问题。线程池类有多种创建线程池对象的方式,ThreadPoolExecutor构造方法是用得最多的。ThreadPoolExecutor构造方法有七个参数,分别是线程池维持线程数,线程池最大线程数,线程活跃时长,任务队列,新线程生产方式,拒绝策略。ThreadPoolExecutor创建的线程池的工作逻辑是这样的:
(1) 如果线程数小于线程池维持线程数,每个任务优先创建一个新线程处理
(2) 如果线程数达到线程池维持线程数,每个任务优先装入任务队列
(3) 如果线程数小于线程池最大线程数且任务队列任务数达到规定上限,创建一个新线程处理
(4) 如果线程数达到最大线程数且任务队列任务数达到规定上限,拒绝新的任务
线程池对象有两个方法接收新任务,一个是submit,另一个是execute。execute方法接收Runnable对象,不能返回线程的运行结果。submit方法可以接收Runnable对象也可以接收Callable对象,返回一个Future对象,可以通过Future对象控制线程的运行状态与得到线程的运行结果。

线程池状态

线程池有5种状态,分别是运行中、关闭、中止、整理、终止。运行中表示线程池对象在正常工作;关闭表示线程池不再接收新的任务但会处理完手头上的任务;中止表示线程池不再接收新的任务且中止手头上的任务;整理表示线程池对依目前工作状态调整线程数量与工作状态;终止表示线程池对象完成工作,结束工作。