详解多线程启动涉及的知识点
Thread类
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易地实现资源共享。实现Runnable接口比继承Thread类所具有的优势:
适合多个相同的程序代码的线程去处理同一个资源
可以避免java中的单继承的限制
增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
//继承 Thread
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
Runnable接口
// 先看一下java.lang.Runnable,它是一个接口,在它里面只声明了一个run()方法:
// 由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。
public interface Runnable {
public abstract void run();
}
//实现Runnable接口
Thread2 mTh = new Thread2();
new Thread(mTh, "C").start();//同一个mTh,但是在Thread中就不可以,如果用同一个实例化对象mt,就会出现异常
new Thread(mTh, "D").start();
new Thread(mTh, "E").start();
这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Callable接口
Callable接口位于java.util.concurrent包下,在它里面也只声明了一个方法,只不过这个方法叫做call()。
public interface Callable<V> {
V call() throws Exception;
}
可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。Callable接口可以看作是Runnable接口的补充,call方法带有返回值,并且可以抛出异常。
FutureTask类
如何获取Callable的返回结果呢?一般是通过FutureTask这个中间媒介来实现的。整体的流程是这样的:
把Callable实例当作参数,生成一个FutureTask的对象
然后把这个对象当作一个Runnable,作为参数另起线程。
由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。下面以Thread包装线程方式启动来说明一下。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo {
public static void main(String[] args) throws Exception {
Callable<Integer> call = new Callable<Integer>() {
public Integer call() throws Exception {
System.out.println("计算线程正在计算结果...");
Thread.sleep(3000);
return 1;
}
};
FutureTask<Integer> task = new FutureTask<>(call);
Thread thread = new Thread(task);
thread.start();
System.out.println("main线程干点别的...");
Integer result = task.get();
System.out.println("从计算线程拿到的结果为:" + result);
}
}
Future接口
FutureTask继承体系中的核心接口是Future。
Future的核心思想是:一个方法,计算过程可能非常耗时,等待方法返回,显然不明智。可以在调用方法的时候,立马返回一个Future,可以通过Future这个数据结构去控制方法f的计算过程。这里的控制包括:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);//还没计算完,可以取消计算过程
boolean isCancelled();///判断计算是否被取消
boolean isDone();//判断是否计算完
V get() throws InterruptedException, ExecutionException;
//获取计算结果(如果还没计算完,也是必须等待的)
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
也就是说Future提供了三种功能:
判断任务是否完成;
能够中断任务;
能够获取任务执行结果。
实现Runnable接口和Callable接口的区别
如果想让线程池执行任务的话需要实现的Runnable接口或Callable接口。
Runnable接口或Callable接口实现类都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。两者的区别在于 Runnable 接口不会返回结果但是 Callable 接口可以返回结果。
备注:工具类 Executors 可以实现 Runnable 对象和 Callable 对象之间的相互转换。
Executors.callable(Runnable task)也就是说Future提供了三种功能:
判断任务是否完成;
能够中断任务;
能够获取任务执行结果。
或
Executors.callable(Runnable task,Object resule )。
记得点「赞」和「在看」↓
爱你们