参考文章
由浅入深理解Java线程池及线程池的如何使用
java常用的几种线程池比较
java线程池与五种常用线程池策略使用与解析
上节中将线程比作货船,一艘货船要报废或者是新买都是一件非常耗费资源的事情,线程的使用也有该问题,当一个线程的任务执行完毕后,该线程会被自然销毁,当有新的任务要驱动时,又必须新建一个新的线程,这个过程对资源消耗是很大的。如下图所示
Executor将为你管理Thread对象,从而简化了并发编程,Executor允许你管理异步任务的执行,而无须显式地管理线程的生命周期。
具体实现是Java中引入了线程池的概念来解决复用线程的问题,一个线程驱动完任务后不会被销毁而是进入闲置状态,当有新任务来时闲置线程又可派上用场。
线程池的实现类->java.util.concurrent.ThreadPoolExecutor
,可用构造方法来创建一个线程池对象
//ThreadPoolExecutor extends AbstractExecutorService
//AbstractExecutorService implements ExecutorService
//interface ExecutorService extends Executor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
其中各个参数代表的含义如下
除了用构造方法创建一个线程池外,Java还提供了一个工厂类java.util.concurrent.Executors
来生成配置好的常用线程池方法,以下是它支持创建的四种类型的线程池。
//Executors
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
//另一重载方法,可传入线程工程对象来决定生成的线程对象
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue(),
threadFactory);
}
CachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,以达到线程都能被尽量复用的目的
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory);
}
FixedThreadPool会生成固定线程数量的线程池,这样可以一次性预先执行代价高昂的线程分配,也不用为每个任务都固定地付出创建线程的开销。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory));
}
SingleThreadExecutor就像是线程数量为1的FixedThreadPool,如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队执行,每个任务都会在下一个任务开始之前运行结束,所有的任务都将使用相同的线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
//执行方法不是execute()
//延迟执行
public ScheduledFuture> schedule(Runnable command, long delay,TimeUnit unit)
//周期执行
public ScheduledFuture> scheduleAtFixedRate(Runnable command,long initialDelay,
long period, TimeUnit unit)
可让任务延迟或者是周期性地执行
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
该线程池生成方法是Java8才加入的,创建一个拥有多个任务队列(以便减少连接数)的线程池。
内部会创建ForkJoinPool,利用Work-Stealing算法,并行处理任务,不保证处理顺序。(摘自杨晓峰《Java核心技术36讲》)
class LiftOff : Runnable{
companion object {
private var taskCount = 0
}
protected var countDown = 5
private val id = taskCount++
constructor(){}
constructor(countDown : Int){
this.countDown = countDown
}
fun status():String{
return "#$id(${if (countDown > 0) this.countDown else "LiftOff!"})"
}
override fun run() {
while (countDown-- > 0){
print(status() + ">"+Thread.currentThread().name+",")
}
}
}
//如不够线程,缓存中又无线程可用,则会创建一个新线程
val exec = Executors.newCachedThreadPool()
//固定线程数量
val exec = Executors.newFixedThreadPool(3)
//单线程执行
val exec = Executors.newSingleThreadExecutor()
for(i in 1..5){
exec.execute(LiftOff())
}
exec.shutdown()
/**output
newCachedThreadPool,线程池会创建跟任务数量一样的线程数共五个
#0(4)>pool-1-thread-1,#0(3)>pool-1-thread-1,#0(2)>pool-1-thread-1,#0(1)>pool-1-thread-1,#0(LiftOff!)>pool-1-thread-1,#1(4)>pool-1-thread-2,#2(4)>pool-1-thread-3,#3(4)>pool-1-thread-4,#2(3)>pool-1-thread-3,#3(3)>pool-1-thread-4,#2(2)>pool-1-thread-3,#3(2)>pool-1-thread-4,#2(1)>pool-1-thread-3,#3(1)>pool-1-thread-4,#2(LiftOff!)>pool-1-thread-3,#3(LiftOff!)>pool-1-thread-4,#1(3)>pool-1-thread-2,#1(2)>pool-1-thread-2,#4(4)>pool-1-thread-5,#1(1)>pool-1-thread-2,#4(3)>pool-1-thread-5,#1(LiftOff!)>pool-1-thread-2,#4(2)>pool-1-thread-5,#4(1)>pool-1-thread-5,#4(LiftOff!)>pool-1-thread-5,
newFixedThreadPool,只会创建3个线程
#0(4)>pool-1-thread-1,#0(3)>pool-1-thread-1,#0(2)>pool-1-thread-1,#0(1)>pool-1-thread-1,#0(LiftOff!)>pool-1-thread-1,#3(4)>pool-1-thread-1,#3(3)>pool-1-thread-1,#3(2)>pool-1-thread-1,#3(1)>pool-1-thread-1,#3(LiftOff!)>pool-1-thread-1,#4(4)>pool-1-thread-1,#4(3)>pool-1-thread-1,#4(2)>pool-1-thread-1,#4(1)>pool-1-thread-1,#4(LiftOff!)>pool-1-thread-1,#1(4)>pool-1-thread-2,#1(3)>pool-1-thread-2,#1(2)>pool-1-thread-2,#1(1)>pool-1-thread-2,#1(LiftOff!)>pool-1-thread-2,#2(4)>pool-1-thread-3,#2(3)>pool-1-thread-3,#2(2)>pool-1-thread-3,#2(1)>pool-1-thread-3,#2(LiftOff!)>pool-1-thread-3,
newSingleThreadExecutor,只有一个线程在顺序执行任务
#0(4)>pool-1-thread-1,#0(3)>pool-1-thread-1,#0(2)>pool-1-thread-1,#0(1)>pool-1-thread-1,#0(LiftOff!)>pool-1-thread-1,#1(4)>pool-1-thread-1,#1(3)>pool-1-thread-1,#1(2)>pool-1-thread-1,#1(1)>pool-1-thread-1,#1(LiftOff!)>pool-1-thread-1,#2(4)>pool-1-thread-1,#2(3)>pool-1-thread-1,#2(2)>pool-1-thread-1,#2(1)>pool-1-thread-1,#2(LiftOff!)>pool-1-thread-1,#3(4)>pool-1-thread-1,#3(3)>pool-1-thread-1,#3(2)>pool-1-thread-1,#3(1)>pool-1-thread-1,#3(LiftOff!)>pool-1-thread-1,#4(4)>pool-1-thread-1,#4(3)>pool-1-thread-1,#4(2)>pool-1-thread-1,#4(1)>pool-1-thread-1,#4(LiftOff!)>pool-1-thread-1,
*/
通过调用方法
java.util.concurrent.ExecutorService#shutdown()
可防止新任务被提交给这个Executor,当前线程将继续运行在shutdown()
被调用之前提交的所有任务。
另一个关闭方法如下,该方法会尝试停止所有正在执行的任务,并返回正在等待执行的任务列表
List shutdownNow();
Runnable是执行工作的独立任务并不返回任何值,如希望在任务完成时返回一个值,那么任务可通过实现Callable接口来实现,Callabel带有一个类型参数的泛型,表示从方法call()中返回值的类型,call()是实现Callable接口需要实现的方法.
实例如下
class TaskWithResult(var id:Int): Callable{
override fun call(): String {
return "result of TaskWithResult$id"
}
}
fun main(args: Array) {
val exec = Executors.newCachedThreadPool()
val results = ArrayList>()
for (i in 0 until 10){
results.add(exec.submit(TaskWithResult(i)))
}
for(fs in results){
try {
println(fs.get())
}catch (e: InterruptedException){
println(e)
return
}catch (e: ExecutionException){
println(e)
return
}finally {
exec.shutdown()
}
}
}
/**output
result of TaskWithResult0
result of TaskWithResult1
result of TaskWithResult2
result of TaskWithResult3
result of TaskWithResult4
result of TaskWithResult5
result of TaskWithResult6
result of TaskWithResult7
result of TaskWithResult8
result of TaskWithResult9
*/
submit()方法会产生Future对象,调用Future的get()方法可获取任务运行的结果,可用isDone()方法来决断任务是否完成,如果任务未完成时调用get()方法,则会阻塞线程,直至结果准备就绪。
由于线程的本质特性,当子线程发生异常而没有被捕获时,在主线程中用try-catch没法捕获子线程逃逸的异常,这样就会造成程序异常退出。
但通过Executor执行的任务,可以通过在生成不同线程池方法中可传入的ThreadFactory来捕获子线程的异常。实现过程是在生成每个Thread对象时附着一个异常处理器Thread.UncaughtException
,Thread.UncaughtExecptionHandler.uncaughtException()
会在线程因未捕获的异常而临近死亡时被调用。
实例如下
class ExceptionThread2: Runnable{
override fun run() {
val t = Thread.currentThread()
println("run() by $t")
println("eh = ${t.uncaughtExceptionHandler}")
throw RuntimeException()
}
}
class MyUncaughtExceptionHandler: Thread.UncaughtExceptionHandler{
override fun uncaughtException(t: Thread?, e: Throwable?) {
println("caught $e") //处理未捕获异常
}
}
class HandlerThreadFactory: ThreadFactory{
override fun newThread(r: Runnable?): Thread {
println("${this}creating new Thread")
val t = Thread(r)
println("created$t")
t.uncaughtExceptionHandler = MyUncaughtExceptionHandler() //这里设置新建线程的未捕获异常处理器
println("eh = ${t.uncaughtExceptionHandler}")
return t
}
}
fun main(args: Array) {
val exec = Executors.newCachedThreadPool(HandlerThreadFactory())
exec.execute(ExceptionThread2())
}
/**output
mutilthread.HandlerThreadFactory@266474c2creating new Thread
createdThread[Thread-0,5,main]
eh = mutilthread.MyUncaughtExceptionHandler@6f94fa3e
run() by Thread[Thread-0,5,main]
eh = mutilthread.MyUncaughtExceptionHandler@6f94fa3e
mutilthread.HandlerThreadFactory@266474c2creating new Thread 这是线程池多创建的线程
createdThread[Thread-1,5,main]
eh = mutilthread.MyUncaughtExceptionHandler@5cded4f1 同时也新建了一个handler的对象
caught java.lang.RuntimeException
*/
实现流程就是自定义ThreadFactory类->自定义UncaughtExceptionHandler类->在ThreadFactory中生成的新线程附着一个UncaughtExceptionHandler对象->未捕获的异常会在自定义UncaughtExceptionHandler类中被处理.
如果想在所有的线程中都实现一样的未捕获异常处理器,则可通过设置Thread的静态域来达到,如下方法所示
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler)
这个处理器只有在不存在线程专有的未捕获异常处理器的情况下才会被调用
一个线程可以处于以下四种状态之一:
以上都是《Java编程思想》中关于线程状态的介绍,但在Java源码中Thread是有六种状态的,
public enum State {
/**新建状态*/
NEW,
/**就绪状态*/
RUNNABLE,
/**阻塞状态*/
BLOCKED,
/**等待状态*/
WAITING,
/**超时等待状态*/
TIMED_WAITING,
/**终止状态*/
TERMINATED;
}
这篇文章详细讲解了关于线程状态的切换,
Java线程的6种状态及切换(透彻讲解)