Java创建线程的三种方式
- 继承Thread
- 实现Runnable
- 实现Callable
继承Thread创建线程
通过继承Thread的方式,可以很快的创建一个线程,Thread中有一个run()方法,只需要重写该方法,将需要另外开启一个线程处理数据(比如一个耗时的计算任务)的逻辑放在里面,然后调用start()方法便可以开启一个新的线程。这里注意调用的是start()方法,如果调用的是run()方法,这个方法是一个普通方法,整个代码的逻辑就和单线程一样。
1 | public class MyThread extends Thread { |
运行结果:1
2
3
4进入:main
退出:main
进入:Thread-0
退出:Thread-0
上面通过继承Thread创建了一个线程,通过Thread方法打印当前线程的名称,并让这个线程睡眠10秒。由于线程睡眠了10秒,在main线程运行结束后,自定义的线程还在运行,可见这是一个独立的线程。
实现Runnable创建线程
通过实现Runnabel接口,实现里面的run()方法,将业务逻辑放在run()方法中,然后将实现的Runnable对象作为参数传递给Thread,调用start()方法,也可以创建线程。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class MyRunnableThread implements Runnable {
/**
* 实现run方法,将业务逻辑放在里面
*/
@Override
public void run() {
System.out.println("进入:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
System.out.println("进入:" + Thread.currentThread().getName());
MyRunnableThread myRunnableThread = new MyRunnableThread();
Thread thread = new Thread(myRunnableThread);
thread.start();
System.out.println("退出:" + Thread.currentThread().getName());
}
}
上面的程序运行结果和继承Thread创建线程一样。如果业务逻辑比较简单,在Java8之后,可以使用lambda表达式实现,代码更加简洁。
实现Callable创建线程
通过实现Callable也可以创建线程,和实现Runnable类似,不同的点是Callable是实现call()方法,这个方法可以返回值或者抛出异常,通过FutureTask,可以获取到返回的值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38public class MyCallableThread implements Callable {
/**
* 实现call方法,将业务逻辑放在里面
*/
@Override
public Object call() {
System.out.println("进入:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出:" + Thread.currentThread().getName());
return "return Data";
}
public static void main(String[] args) {
System.out.println("进入:" + Thread.currentThread().getName());
MyCallableThread callableThread = new MyCallableThread();
FutureTask futureTask = new FutureTask(callableThread);
new Thread(futureTask).start();
try {
System.out.println("自定义线程返回的值为:" + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("退出:" + Thread.currentThread().getName());
}
}
运行结果:1
2
3
4
5进入:main
进入:Thread-0
退出:Thread-0
自定义线程返回的值为:return Data
退出:main
可以看到,通过FutureTask,可以拿到线程中返回的值,但是一旦使用了get()方法,就会让获取返回值的线程阻塞,知道计算线程返回结果,那这样和在同一个线程中计算有什么区别呢?区别在于主线程可以同步做一些可以并行的操作,节约时间。
总结
上面的三种方式都可以创建一个线程,既然这样,为什么还需要三种方式呢?首先通过Callable这种方式可以获取到返回值,这个特点比较明显。而继承Thread和实现Runnable这两种方式的区别主要是两点:(1)由于Java不能多继承,而可以多实现,这样通过实现的方式,线程类可以继承其他的类。(2)Runnable其实是一个任务,并不是线程本身,而是将任务作为参数传递给线程。可以将复杂的计算任务封装到Runnable中,使用多个线程进行计算。当然,这里由于是多个线程计算同一个任务,就不免涉及到多线程的问题。