Java创建线程的方式

一些创建线程的方式

如何创建线程

一般来说,创建线程有很多种方式,例如继承Thread类、实现Runnable接口、实现Callable接口、使用线程池、使用CompletableFuture类等等。

不过,这些方式其实并没有真正创建出线程。准确点来说,这些都属于是在 Java 代码中使用多线程的方法。

严格来说,Java 就只有一种方式可以创建线程,那就是通过new Thread().start()创建。不管是哪种方式,最终还是依赖于new Thread().start()

继承Tread类

这是最普通的方式,继承Thread类,重写run方法,如下:

1
2
3
4
5
6
7
8
9
10
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println("1......");
}

public static void main(String[] args) {
new ExtendsThread().start();
}
}

实现Runnable接口

这也是一种常见的方式,实现Runnable接口并重写run方法,如下:

1
2
3
4
5
6
7
8
9
10
11
public class ImplementsRunnable implements Runnable {
@Override
public void run() {
System.out.println("2......");
}

public static void main(String[] args) {
ImplementsRunnable runnable = new ImplementsRunnable();
new Thread(runnable).start();
}
}

与继承Thread接口不同的是,Java当中是单继承,当一个类继承了Thread类后就无法再继承其他的类,通过实现Runnable接口并重写run方法,可以再实现继承其他类。

实现Callable接口

和上一种方式类似,这里需要去重写Callable接口的call方法,并配合FutureTask使用,只不过这种方式可以使用get()方法拿到线程执行完的返回值,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ImplementsCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("3......");
return "Justin";
}

public static void main(String[] args) throws Exception {
ImplementsCallable callable = new ImplementsCallable();
FutureTask<String> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}

使用ExecutorService线程池

这种属于进阶方式,可以通过Executors创建线程池,也可以自定义线程池,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UseExecutorService {
public static void main(String[] args) {
ExecutorService poolA = Executors.newFixedThreadPool(2);
poolA.execute(()->{
System.out.println("4A......");
});
poolA.shutdown();

// 又或者自定义线程池
ThreadPoolExecutor poolB = new ThreadPoolExecutor(2, 3, 0,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
poolB.submit(()->{
System.out.println("4B......");
});
poolB.shutdown();
}
}

注意点

创建线程的方式十分多样,最少都能有上面的四种答案,那这真的对吗?可以说对,但严格意义上来说,又不对。

抛开后面一些先不谈,咱们就聊最开始的三种:“继承Thread类、实现Runnable接口、实现Callable接口”,这应该是广为人知的答案。

那么此时来看个例子:

1
2
3
4
5
6
7
public class ImplementsRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ ":小猫爱吃鱼");
}
}

这里定义了一个类,实现了Runnable接口并重写了run方法,按前面的说法,这种方式是不是创建了一条线程?答案是Yes,可问题来了,请你告诉我,该如何启动这条所谓的“线程”呢?

1
2
3
4
public static void main(String[] args) {
ImplementsRunnable runnable = new ImplementsRunnable();
runnable.run();
}

难道像上面这样嘛?来看看运行结果:

1
main:小猫爱吃鱼

结果很显然,打印出的线程名字为:main,代表目前是主线程在运行,和调用普通方法没任何区别,那究竟该如何创建一条线程呀?要这样做:

1
2
3
4
public static void main(String[] args) {
ImplementsRunnable runnable = new ImplementsRunnable();
new Thread(runnable).start();
}

newRunnable对象,接着再new一个Thread对象,然后把Runnable丢给Thread,接着调用start()方法,此时才能真正意义上创建一条线程,运行结果如下:

1
Thread-0:小猫爱吃鱼

此时线程名字变成了Thread-0,这意味着输出“小猫爱吃鱼”这句话的代码,并不是main线程在执行了,实现了Runnable接口的ImplementsRunnable类,并不能被称为一条线程,包括所谓的Callable、FutureTask……,都不能创建出真正的线程。

换到前面所提出的三种方式中,只有继承Thread类,才能真正创建一条线程,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ExtendsThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ ":小猫爱吃鱼");
}

public static void main(String[] args) {
new ExtendsThread().start();
}
}

// 运行结果:
// Thread-0:竹子爱熊猫

因为当你用一个类,继承Thread类时,它内部所有的方法,都会被继承过来,所以当前类可以直接调用start()方法启动,更具体点来说,Java中,创建线程的方式就只有一种:调用Thread.start()方法!只有这种形式,才能在真正意义上创建一条线程!