在java中创建一个线程有3种方法。

一.直接new一个Thread对象

1
2
3
4
5
6
7
8
9
public void testForCreateThread1(){
Thread thread1 = new Thread("t1") {
public void run() {
System.out.println("t1 is doing");
}
};

thread1.start();
}

二.通过Runnable接口辅助创建线程

1
2
3
4
5
6
7
8
9
10
11
public void testForCreateThread2(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("t2 is doing");
}
};

Thread thread2 = new Thread(runnable,"t2");
thread2.start();
}

针对第二种方法可以通过lambda对其简化

1
2
3
4
5
public void testForCreateThread3(){
Runnable runnable = () -> System.out.println("t3 is doing");
Thread thread3 = new Thread(runnable, "t3");
thread3.start();
}

疑问

以上代码中可以发现有new Runnable这个操作,Runnable是一个接口,为啥能够被实例化?

–解答:在java中接口和抽象类是不能被实例化的,在这里也不例外.而这里其实是使用了匿名内部类,也就是说new出来的这个对象不是Runnable,而是一个实现了该接口的子类,这也是为什么run方法上出现@Override注解的原因。等价于以下代码

1
2
3
4
5
6
7
8
9
10
11
12
public class Mythread implements Runnable{
public void run(){
System.out.println("t2 is doing");
}
}

public void testForCreateThread2(){
Runnable runnable = new Mythread();

Thread thread2 = new Thread(runnable,"t2");
thread2.start();
}

可以发现,相较于使用匿名内部类,后者需要额外创建一个类,更加麻烦。因此对于那些只在某些只使用一次的接口实现子类,可以使用匿名内部类的方式创建,这也是我们经常使用的方式,比如在给集合排序时,就会使用匿名内部类来实现Comparator接口。

三.通过FutureTask辅助创建线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void testForCreateThread4() throws ExecutionException, InterruptedException {
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
public Integer call() throws InterruptedException {
System.out.println("t4 is doing");
Thread.sleep(1000);
return 10;
}
});

Thread thread4 = new Thread(task, "t4");
thread4.start();
int t = task.get();
System.out.println(t);
}

​ FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable接口,因此FutureTask也实现了Runnable接口。

​ FutureTask类中提供了两个构造方法

1
2
3
4
5
6
7
8
9
10
11
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}

public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

​ 第一种构造方法的Callable也是一个接口,里面有一个抽象方法call(),用于返回计算结果.FutureTask的get方法能够获取该返回值,需要注意的是该get方法是一个阻塞式线程,它会一直等待这个返回值,所以如果你将task.get()语句放在thread4.start()语句之前,就会发生死锁现象。

​ 需要注意的是,以上3中创建线程的方法中,其实只有第一种方法才是真正创建了一个线程,后两者只是创建了线程所要运行的任务,即run方法中的内容,不过由于后两种方法能够将创建线程和线程要运行的任务分开,是编程更加灵活,因此也更受青睐。