线程是什么,线程是一个任务的工作单元和执行单元。我们在用线程的时候知道,要创建线程,再执行线程。如果任务多的情况呢,会有大量的创建销毁线程的资源消耗,这时候就引入了线程池的概念。
JDK5开始,把工作单元和执行单元分开,工作单元包括Runnable和Callable,利用Executor来执行任务。
通过 Executor来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。
this 逃逸是指在构造函数返回之前其他线程就持有该对象的引用,调用尚未构造完全的对象的方法可能引发令人疑惑的错误。
1、任务: 包括被执行任务需要实现的接口,Runnable接口或者Callable接口。
2、任务的提交和执行: 包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduleThreadPoolExecutor)。ExecutorService接口中补充了任务的提交方法submit()。
3、任务的结果(异步计算): 包括接口Future和实现Future接口的FutureTask类。
Executor翻译过来就是执行器的意思,也就是说,Executor框架的目的就是调度任务进行执行,并得出异步计算的结果。
主线程通过实现 Runnable 或者 Callable 接口,创建任务对象,用于后续执行。
Runnable或Callable接口的实现类都可以被 ThreadPoolExecutor 或ScheduledThreadPoolExecutor 执行。
区别:Runnable接口不会抛异常或者返回对象,但是 Callable 接口可以。
代码如下:
@FunctionalInterface
public interface Callable {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}
@FunctionalInterface
public interface Runnable {/*** When an object implementing interface Runnable
is used* to create a thread, starting the thread causes the object's* run
method to be called in that separately executing* thread.* * The general contract of the method run
is that it may* take any action whatsoever.** @see java.lang.Thread#run()*/public abstract void run();
}
Executor常用的类图如下:
任务执行机制的核心接口 Executor源码如下:
public interface Executor {/*** Executes the given command at some time in the future. The command* may execute in a new thread, in a pooled thread, or in the calling* thread, at the discretion of the {@code Executor} implementation.** @param command the runnable task* @throws RejectedExecutionException if this task cannot be* accepted for execution* @throws NullPointerException if command is null*/void execute(Runnable command);
}
通过代码可以看出,其中定义了任务的执行。
execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
ExecutorService接口中提供了submit()方法,用于提交需要返回值的任务。
代码如下:
Future submit(Callable task);
Future submit(Runnable task, T result);
Future> submit(Runnable task);
线程池会返回一个 Future 类型的对象。
Future存储的是未来产生的一个结果,其中的get()方法是阻塞的,用来获取返回值。
代码如下:
V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
使用 get(long timeout,TimeUnit unit)方法的话,如果在 timeout 时间内任务还没有执行完,就会抛出 java.util.concurrent.TimeoutException。
FutureTask接口表示Future+Runnable,其类图如下:
FutureTask表示可以直接创建一个FutureTask对象,交给线程池去执行,并且有返回结果。
下面这个图出现的次数很多了:
1、主线程创建任务对象,这个任务对象实现了Runnable或 Callable接口。
2、把任务对象交给执行器ExecutorService去执行:
ExecutorService.execute(Runnable command);
Future submit(Callable task);
Future submit(Runnable task, T result);
Future> submit(Runnable task);
3、主线程可以直接创建一个FutureTask对象交给执行器去执行。
4、主线程可以执行 FutureTask.get()方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。
submit()和get()方法:
public static void main(String[] args) throws ExecutionException, InterruptedException {//线程池ExecutorService executorService = Executors.newFixedThreadPool(3);//提交并执行任务Future submit = executorService.submit(() -> {try {Thread.sleep(5000L);System.out.println("5秒过去啦");} catch (InterruptedException e) {e.printStackTrace();}return "我是返回值";});String s = submit.get();System.out.println(s);//关闭线程池executorService.shutdown();}
结果如下:
5秒过去啦
我是返回值
get(long timeout,TimeUnit unit)方法:
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {//线程池ExecutorService executorService = Executors.newFixedThreadPool(3);//提交并执行任务Future submit = executorService.submit(() -> {try {Thread.sleep(5000L);} catch (InterruptedException e) {e.printStackTrace();}return "我是返回值";});//3秒就不行了哦String s = submit.get(3, TimeUnit.SECONDS);System.out.println(s);//关闭线程池executorService.shutdown();}
运行结果如下:
抛出异常,且线程阻塞未结束。
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {//创建任务Callable callable = new Callable() {@Overridepublic String call() throws Exception {return "我是callable";}};//线程池ExecutorService executorService = Executors.newFixedThreadPool(3);//提交并执行任务Future submit = executorService.submit(callable);//3秒就不行了哦String s = submit.get(3, TimeUnit.SECONDS);System.out.println(s);//关闭线程池executorService.shutdown();}
执行结果如下:
我是callable
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {//线程池ExecutorService executorService = Executors.newFixedThreadPool(3);//创建并执行任务FutureTask task = new FutureTask<>(()->{return "aaaaaaa";});executorService.submit(task);String s = task.get();System.out.println(s);//关闭线程池executorService.shutdown();}
执行结果如下:
aaaaaaa
前者无返回值和抛异常,后者有。
前者执行不需要返回值的任务,后者执行需要返回值的任务,返回值为Future。
shutdown() :关闭线程池,线程池的状态变为 SHUTDOWN。线程池不再接受新任务了,但是队列里的任务得执行完毕。(新任务停止,已存在的要执行完)
shutdownNow():关闭线程池,线程的状态变为 STOP。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。(停止所有任务并返回等待值)
isTerminated () :调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true。
isShutdown():调用 shutdown() 方法后返回为 true。