1、创建线程的方式
(1)、继承Thread
(2)、实现Runnable接口
(3)、实现Callable接口
(4)、线程池
2、第一和第二中的新建线程方式较为常见,与Callable创建线程的区别:
(1)、线程执行完成后,是否可以返回值。
前者没有,后者有
(2)、线程执行时遇到异常是否会抛
前者不会,后者会
(3)、实现方法的不同
前者是实现run方法,后者是实现call方法
3、使用Callable创建线程
由于Thread中的构造方法中没有callable的构造函数,所有不能用Thread直接创建,需要寻找一个中间变量,就是既认识Runnable也认识Callable创建的线程,它就是futuretaske叫未来任务,从jdk的开发文档中可以看到都有这两个的构造函数。
4、列子
package com.study.demo.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class CreateRun implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"进入到run方法!");
}
}
class CreatCall implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"进入到call方法!");
return 1234;
}
}
public class Demo1 {
public static void main(String[] args) {
//创建runnable实现的线程
new Thread(new CreateRun(),"AA").start();
//创建callable实现的线程
//由于Thread中的构造方法中没有,callable的构造函数,所有不能用Thread直接创建
//创建Callable的时候,需要寻找一个中间变量,就是既认识Runnable也认识Callable创建的线程
//它就是futuretaske叫未来任务
FutureTask<Integer> futureTask = new FutureTask<>(new CreatCall());
new Thread(futureTask,"BB").start();
while (!futureTask.isDone()) {
System.out.println("等待BB线程执行完成!");
}
//lam表达式的写法:
FutureTask<Integer> futureTask1 = new FutureTask<>(()-> {
System.out.println(Thread.currentThread().getName()+"进入到lam表达式写法的线程");
return 4567;
}) ;
new Thread(futureTask1,"CC").start();
try {
System.out.println("获取CreatCall线程执行结束的返回值:"+futureTask.get());
System.out.println("获取lam表达式的写法线程执行结束的返回值:"+futureTask1.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
小结(重点)
• 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成, 当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态
• 一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果
• 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。
• 只计算一次