1、继承Thread类,覆盖重写run方法
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("创建线程的第一种方法:继承Thread类");
}
}
2、实现Runnable接口,覆盖重写run方法
Runnable r=new Runnable() {
@Override
public void run() {
System.out.println("创建线程的第二种方式:实现Runnable接口");
}
};
new Thread(r).start(); //将r注入线程,并启动
3、实现Callable接口,覆盖重写call方法
万变不离其宗,创建线程本质上还是要创建Thread类。
可以通过无参构造创建(方法1),也可以通过有参构造,传入Runnable类型的对象(方法2)
Thread t =new Thread();
那么Runnable和Callable有什么联系呢???
简单画了个图表示他们之间复杂的关系。总的来说就是:
(1)要构造Thread,就要传入Runnable类型的对象;
(2)FutureTask<>间接继承实现了Runnable接口,所以可以传入FutureTask<>;
(3)要构造FutureTask<>,就要传入Callable类型的对象;
(4)Callable是一个接口不能实例化,所以可以传入Callable实现类的对象。
(可以用匿名内部类实现Callable接口)
代码就是这样写:
Callable<String> c=new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("创建线程的第三种方法:实现Callable接口");
return "返回"; //返回到哪,一会再说
}
};
FutureTask<String> ft=new FutureTask<String>(c);
new Thread(ft).start();
这样,我们调用线程的strat()方法就可以运行run方法了。
但是,run()在哪呢???
回想一下,Runnable方法的run被我们覆盖重写了,FutureTask类型的ft里面应该也有run方法,这样我们启动线程的时候才能做任务。那我们去看看FutureTask里的源码找找run方法:(第254行)
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call(); //!!!!!!!!!!!!!!!!!
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);//!!!!!!!!!!!!!!
}
可以看到,run方法里面接收了call的返回值并赋值给result,最后调用set方法传入了result
看看set是啥:
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v; //!!!!!!!!!!!!!!!!!!!!!!
STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
set里面传入的泛型v起始就是result,把这个result赋值给outcome。outcome是FutureTask类的成员变量。
再拓展一下,由set就有get,那我们去源码里找找get,看看get做了啥:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s); //!!!!!!!!!!!!!!!!!!!!!1
}
get的返回值是泛型,调用了report方法,那再看看report:(快结束了再坚持一下……)
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x; //!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
report的返回值也是泛型,返回x,x就是outcome!!!
好像串起来了!!!
也就是说,我们以前用Runnable方法时传入的对象r,这个r没有什么特有的方法了。
但是我们用FutureTask间接继承实现的Runnable方法传入的对象ft有很多自带的方法,有run方法、set方法、get方法等等,这个get方法可以获取传入FutureTask中Callable对象的返回值(晕)
我们把试一下get方法:
Callable<String> c=new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("创建线程的第三种方法:实现Callable接口");
return "实现接口";
}
};
FutureTask<String> ft=new FutureTask<String>(c);
Thread t=new Thread(ft);
t.start();
System.out.println(ft.get());
输出结果是:
4、线程池创建
其实也是Runnable方法,让线程池中的线程执行
Runnable r=new Runnable() {
@Override
public void run() {
System.out.println("创建线程的第四种方法:线程池创建");
}
};
ExecutorService es=Executors.newFixedThreadPool(2);
es.execute(r);