创建线程的四种方式
一.通过重写Thread类中的run方法
public class greatThread {
public static void main(String[] args) throws InterruptedException {
//1.通过继承Thread创建
threadTest threadTest1 = new threadTest();
threadTest1.start();
}
static class threadTest extends Thread{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"通过继承Thread,重写run方法来创建线程");
}
}
}
二.通过传递一个Runnable对象来创建
public class greatThread {
public static void main(String[] args) throws InterruptedException {
//2.通过实现Runnable来创建线程
Thread threadTest2 = new Thread(new Runnable(){
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"通过实现Runnable接口创建线程");
}
});
threadTest2.start();
}
}
前两种方法其实是类似的,方法一是通过重写Thread的run方法,方法二则是传递一个重写了run方法的对象给Thread的构造函数。本质上都是对Thread的run方法进行重写,我们可以看一下Thread的构造函数和run函数
//Thread的构造函数,可以传入Runnable对象也可以不传
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
//Thread类中的run方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
可以看出,线程本质上是执行内部的run方法。实现run方法有两种途径
一.重写Thread的run方法;
二.传入一个实现run方法的runnable对象,执行run方法时会去调用target.run();
所以方法一和方法二的本质都是实现Thread内部的run方法。
三.通过Furture、Callable和线程池
前面两种方式创建的线程都有一个缺点,就是不能获取线程执行的状态或者结果。
方法三和方法四本质上是对前两种方法的加强,通过这种方法创建线程可以获取线程的执行状态或者结果。且增加了一种处理异常的方式(看我写的多线程异常处理文章)
首先了解一下callable这个接口
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;
}
和Runnable接口类似,但是它的优点是可以有返回值。
我们在来看一下ExecutorService中的submit方法,向submit方法传递一个callable对象,就会返回一个对应的Furture对象,这个Furture对象可以获取线程执行的部分信息或者返回结果
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
我们来看一下Furtrue这个接口,这个接口有五个方法,方法功能在下面有说明
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;
}
- cancel:取消当前任务,如果任务还未执行返回true,如果任务已经执行完成返回false。如果任务正在执行,传入的mayInterruptIfRunning参数是true返回true,是false则返回false。
- iscancel:判断任务是否取消
- isDone:判断任务是否完成
- V get:获取任务执行完成的返回值,如果任务还在执行则一直阻塞,直到任务执行完成获得返回值
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
通过这种方法创建线程可以获得一个Furture对象来获取线程执行的相关信息
public class greatThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Integer> future = executorService.submit(new Callable<Integer>() {
//创建一个从0加到100的任务
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 0 ; i < 100 ; i++){
sum =sum+i;
}
return sum;
}
});
//获得执行任务的结果
int result = future.get();
System.out.println(result);
}
方法四.通过FutureTask
我们先来看一下FutureTask的实现:
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
public class greatThread2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 0 ; i < 100 ; i++){
sum = sum+i;
}
return sum;
}
});
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(futureTask);
executor.shutdown();
try {
//通过futureTask获取线程执行结果
int result = futureTask.get()
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
五.总结
其实这四种方法本质都是重写Run方法,submit相当于封装了一层,最后都会去调用excute方法去执行FutureTask里面的run方法。
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
可以看出,submit方法相对于excute就是封装了一层,将传入的对象构造成FutureTask对象,FutureTask实现了Runnable接口和Future接口,即可以将其作为任务执行,也可以作为Furture对象观察控制任务的执行。
链接: 参考文章.