Java创建线程的四种方式及其对比

Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口、使用线程池ExecutorService…

1、继承Thread类

  • 创建一个类继承Thread类并重写run()方法
  • 实例化该类new MyThread()
  • 调用线程的start()方法启动线程
  • run()作为线程执行体被执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyThread extends Thread {
// 重写Thread类的run方法
public void run() {
System.out.println("线程启动..." + Thread.currentThread().getName());
}
}

public class ThreadDemo {
public static void main(String[] args) {
// 方法1:对同一个对象创建不同的线程
MyThread myThread = new MyThread();
for (int i = 0; i < 3; i++) {
new Thread(myThread).start();
}

// 方法2:对不同对象创建不同的线程
for (int i = 0; i < 3; i++) {
new Thread(new MyThread()).start();
}
}
}

执行结果:

1
2
3
4
5
6
线程启动...Thread-1
线程启动...Thread-5
线程启动...Thread-3
线程启动...Thread-2
线程启动...Thread-7
线程启动...Thread-9

2、实现Runnable接口

  • 创建一个类实现Runnable接口并重写run()方法
  • 实例化该类new MyThread()
  • 调用线程的start()方法启动线程
  • run()作为线程执行体被执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyThread implements Runnable {
@Override
public void run() {
System.out.println("线程启动..." + Thread.currentThread().getName());
}
}

public class RunnableDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 3; i++) {
new Thread(myThread).start();
}
}
}

执行结果:

1
2
3
线程启动...Thread-0
线程启动...Thread-2
线程启动...Thread-1

3、实现Callable接口

  • 创建Callable接口的实现类,重写call()方法作为线程执行体,该方法有返回值
  • 创建Callable接口类的实例,使用FutureTask类来包装该实例
  • FutureTask对象作为Thread类的target创建启动线程
  • 使用FutureTaskget()方法获取子线程call()方法的返回值。主线程将在get()方法处阻塞等待子线程执行完成。
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
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String> {
// 重写call方法作为线程执行体
public String call() throws Exception {
System.out.println("线程启动..." + Thread.currentThread().getName());
Thread.sleep(2000);
return "线程返回值";
}
}

public class CallableDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
FutureTask<String> futureTask = new FutureTask<String>(myThread);
new Thread(futureTask).start();

try {
// 主线程将会在这里阻塞等待子线程执行完成
// 主线程将获取到子线程的返回值并输出
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果:

1
2
线程启动...Thread-0
线程返回值

4、使用线程池ExecutorService

  • 调用Executors的静态工厂方法创建一个ExecutorService线程池
  • 创建RunnableCallable实现类的实例,作为线程任务
  • 使用ExecutorService对象的submit()方法提交RunnableCallable的实例
  • 使用ExecutorService对象的shutdown()方法关闭线程池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyThread implements Runnable {
@Override
public void run() {
System.out.println("线程启动..." + Thread.currentThread().getName());
}
}

public class ExecutorServiceDemo {
public static void main(String[] args) {
// 创建容纳3个线程的可重用线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
// 向线程池提交线程
pool.submit(new MyThread());
}
// 关闭线程池
pool.shutdown();
}
}

执行结果:

1
2
3
线程启动...pool-1-thread-2
线程启动...pool-1-thread-3
线程启动...pool-1-thread-1

多线程的一些常见问题

继承Thread类和实现Runnable接口的区别?

  • 由于Java只支持单继承,在实现Runnable接口后还可以继承其他类,而继承Thread类之后就不能在继承其他类了。所以一般使用实现Runnable接口的方法实现多线程。

Callable接口和Runnable接口的区别?

  • Callable接口的线程执行体是call(),而Runnable接口的线程执行体是run()
  • call()方法可以有返回值,也可以抛出异常。可以把Callable接口看做Runnable接口的增强版