一、创建线程的方法
常见的四种创建线程的方式:Thread、Runnable接口、线程池、Callable接口
- 1、
通过继承Thread类实现
特点:多个线程之间无法共享该线程类的实例变量。重写run方法,用start方法启动线程 - 2、
实现Runnable接口
特点:较继承Thread类,避免继承的局限性,适合资源共享。需要实现不返回任何内容的run()方法,然后用new Thread(Runnable runnable).start()方法来启动 - 3、
创建线程池实现
特点:线程池提供了一个线程队列,队列中保存所有等待状态的线程,避免创建与销毁额外开销,提高了响应速度 - 4、
实现Callable接口
特点:方法中可以有返回值,并且抛出异常。此方式需要配合Future接口使用,实现在完成时返回结果的call()方法。使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务产生结果,而Future用来获得结果
二、Callable接口与Futrue接口定义
Callable接口定义如下:
@FunctionalInterface
public interface Callable<V> {
/**
* 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;
}
Future提供了3种功能:(1)能够中断执行中的任务、(2)判断任务是否执行完成、(3)获取任务执行完成后的结果。
Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java线程Future模式的实现,可以来进行异步计算。
Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。
Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。
Future接口定义如下:
public interface Future<V> {
// 尝试取消此次任务 mayInterruptIfRunning - true如果执行该任务的线程应该被中断,否则正在进行的任务被允许完成
boolean cancel(boolean mayInterruptIfRunning);
// 如果此任务在正常完成之前被取消,则返回 true 。
boolean isCancelled();
// 返回true如果任务已完成。 完成可能是由于正常终止,异常或取消 - 在所有这些情况下,此方法将返回true 。
boolean isDone();
// 等待计算完成然后返回结果
V get() throws InterruptedException, ExecutionException;
// 在指定的时间之内进行等待,超时不等待
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
三、使用方法
使用Callable和Future来获取任务结果的两种常用写法例子如下:
public class TestMain {
public static void main(String[] args) throws Exception {
// 方式一:不直接构造Future对象,使用ExecutorService.submit方法来获得Future对象,submit方法即支持以Callable接口类型,也支持Runnable接口作为参数
// 定义线程池,可以为多种不同类型线程池
ExecutorService executor = Executors.newCachedThreadPool();
// 提交执行任务
Future<String> future = executor.submit(new AddNumberTask());
// 获取线程执行结果
String str = future.get();
// 打印结果
System.out.println(str);
// 关闭线程池
if (executor != null) {
executor.shutdown();
}
// =======================分割线=======================
// 方式二:使用FutureTask来处理任务。FutureTask类除了实现Future接口外还实现了Runnable接口,所以可以直接提交给Executor执行
// 定义线程池,可以为多种不同类型线程池
ExecutorService executor = Executors.newCachedThreadPool();
// 提交执行任务
FutureTask<String> future = new FutureTask<String>(new AddNumberTask());
// 执行任务
executor.execute(future);
// 获取线程执行结果
String str = future.get();
// 打印结果
System.out.println(str);
// 关闭线程池
if (executor != null) {
executor.shutdown();
}
}
}
// 线程执行的任务类,此处换成实现Runnable接口重写run方法也是一样的用法也可以通过Future取得线程执行完后的结果
class AddNumberTask implements Callable<String> {
public AddNumberTask() {
// 可通过构造传参在call方法中使用 do something
}
// 返回类型由实现Callable接口的泛型决定
@Override
public String call() throws Exception {
// 具体执行的业务逻辑
System.out.println(">>>" + taskNum + "任务启动");
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
System.out.println(">>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,当前任务时间:" + time + "毫秒";
}
}
参考文献
[1]https://www.cnblogs.com/guanbin-529/p/11784914.html
[2]https://www.cnblogs.com/binghe001/p/12321885.html
[3]https://www.cnblogs.com/lililixuefei/p/13185986.html
[4]https://www.jianshu.com/p/f17d300bf4a6
[5]https://zhuanlan.zhihu.com/p/102656503
[6]https://blog.csdn.net/qq_38345606/article/details/82829400
[7]https://www.cnblogs.com/jenson138/p/4919497.html