0、目录
一、介绍
二、工作原理
三、使用
四、Java中自动的四种线程池
五、Future和FutureTask
六、总结
一、介绍
二、工作原理
首先介绍线程池的类图:
ThreadPoolExecutor类是线程池的真正实现类,可以根据不同的需求配置相关的参数,从而实现自定义线程池。(一般不需要自定义线程池,Java内部已经实现了四个常用的线程池,且都已经配置好了相关的参数)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各个参数描述如下:
线程池的工作逻辑:
三、使用
1、创建线程池
//创建时,需要自己配置相关的参数(除非需要,一般不这么创建,Java中已有四种常见的线程创建方法)
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler);
2、向线程池提交要执行的任务
a、execute()方法传入Runnable对象
executor.execute(new Runnable(){
@ovveride
public void run(){
//新线程中要执行的任务
}
});
或b、submit()方法传入Runnable对象
executor.submit(new Runnable(){
@override
public void(){
//新线程中要执行的任务
}
});
或c、submit()方法传入Callable对象
executor.submit(new Callable
execute()和submit()区别
1、定义和具体实现位置不同。execute(Runnable command)归属于Executor接口;submit(Callable
execute()方法在ThreadPoolExecutor类中具体实现,submit()方法在AbstractExecutorService类中具体实现。
2、submit()有返回值,而execute()没有返回。返回值类型为Future,可以根据返回结果查看任务执行情况。submit()方便做异常处理,通过Future.get()可以捕获异常。
Runnable于Callable的区别
定义如下:
public interface Runnable {
void run();
}
public interface Callable {
V call() throws Exception;
}
通过定义可以看到:
1、Runnable执行的方法是run(),Callable执行的方法是call()
2、实现Runnable接口的任务线程没有返回值,而实现Callable接口的任务能返回执行结果
3、call()方法能够抛出异常,而run()方法则不能抛出异常,如果出现异常智能内部处理。
3、关闭线程池
executor.shutdown();
或 executor.shutdownNow();
shutdown()于shutdownNow()的区别:
shutdown:不再接收新的任务,同时将线程池的状态设置为SHUTDOWN状态,已经添加的任务会继续执行直到完成(包括已经加入到任务队列中的);
shutdownNow:不再接收新的任务,将线程池的状态设置未STOP状态,会尝试停止正在执行的任务,并返回那些没有被执行的任务。
这里也仅仅是试图终止正在执行的线程,源码中是通过Thread.interrupt()方法来实现的,而interrupt()方法有一定的局限性,它仅仅是为线程设定了一个状态而已,并不代表就能立即停止正在执行的任务,所以要小心。
四、Java中自动的四种线程池
这四种线程池是:
Executors.newCachedThreadPool():可缓存的线程池
Executors.newFixedThreadPool():定长的线程池
Executors.newScheduledThreadPool():定时的线程池
Executors.newSingleThreadExecutor():单线程线程池
Executors是一个工具类,类似于Collections,提供工厂方法来创建不同类型的线程池。
4.1、可缓存的线程池(Executors.newCachedThreadPool())
定义如下:
可以看到核心线程数量为0,线程最大数量为Integer.MAX_VALUE,也就是说这类线程池没有核心线程,只有非核心线程,且数量不固定,可以无限大,任务到来后可立即执行,不需等待,空闲线程默认保活60s,之后会被回收。适合执行大量且耗时少的线程任务。不过如果线程无限增长,会导致内存溢出。
4.2、定长线程池(Executors.newFixedThreadPool())
定义如下:
可以看到它的corePoolSize = maximumPoolSize,也就是说它只有核心线程而没有非核心线程,每个线程池中最多只能有nThreads个线程,即可控制线程最大并发数量,超出的线程会在队列中等待。缺点就是大小固定,难以扩展。
4.3、定时的线程池(Executors.newScheduledThreadPool())
定义如下:
super()指的是:
可以看大到核心线程数量是固定的,非核心线程的数量是Integer.MAX_VALUE,可以说是无限大,主要应用于执行定时或周期性的任务
使用:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
//延时100毫秒后执行任务
executor.schedule(new Runnable() {
@Override
public void run() {
//任务
}
}, 100, TimeUnit.MICROSECONDS);
//延时0毫秒,每100毫秒执行一次
executor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//任务
}
}, 0, 100, TimeUnit.MICROSECONDS);
4.4、单线程化的线程池(Executors.newSingleThreadExecutor())
定义如下:
可以看到,corePoolSize = maxinumPoolSize = 1, 也就是只有一个核心线程,总线程数量为1,没有非核心线程,因为只有一个核心线程,所以可以保证所有任务按照指定的顺序在一个线程中执行,不需要处理线程同步的问题。它不适合做并发。
四种线程池的总结对比:
五、Future和FutureTask
1、Future
Future表示对执行任务的一些操作,比如取消任务,查询是否取消或完成,获取执行返回结果等,这个结果就是call()方法返回的值。常见方法有cancel(Boolean),isCancelled(), isDone(), get()等。
使用如:
ExecutorService executor = Executors.newCachedThreadPool();
Future future = executor.submit(new Callable() {
@Override
public String call() {
return "haha...";
}
});
try {
String str = future.get();
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.getMessage();
}
Toast显示为“haha...”
2、FutureTask
定义:
通过源码可以看到FutureTask实现了RunnableFuture
而且FutureTask还可以包装Runnable和Callable
可以看到,传入的Runnable会通过Executors.callable()方法转换为Callable型,即FutureTask最终都要执行Callable类型的任务。
由于FutureTask实现了Runnable,因此它就可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行,并且FutureTask实现了Future,所以可以直接通过get()方法获取执行结果。因此FutureTask既是Future、Runnable,又包装了Callable(如果是Runnable则会被转换成Callable)。
FutureTask的使用:
FutureTask taskCallable = new FutureTask(new Callable() {
@Override
public String call() throws Exception {
return "FutureTask(Callable)";
}
});
//或:
String result = "taskRunnable";
FutureTask taskRunnable = new FutureTask(new Runnable() {
@Override
public void run() {
//任务
}
}, result);
//启动任务:(因为其继承Runnable,所以可以采用以下方式)
new Thread(taskRunnable).start();
new Thread(taskCallable).start();
//获取任务执行的结果:(因为继承了Future,所以可以获得执行结果,设置取消,查询是否取消或执行完成)
taskCallable.get();
taskRunnable.get();
六、总结
终于写完啦,哈哈哈哈哈哈哈哈哈
线程池在每一个应用中都会用到,但是要真的全面的去整理一遍,真的不容易,自己用了好几天,也建议您自己整理一遍,通过整理能够加深理解,加深记忆。