Java线程创建方式

一般来说我们比较常用的有以下三种方式,下面介绍它们的使用方法。

1 继承Thread类

通过继承 Thread 类,并重写它的 run 方法,就可以创建一个线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ExtendThread extends Thread {

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "is running...");
}

public static void main(String[] args) {
// 创建线程
ExtendThread thread = new ExtendThread();
// 设置线程名称
thread.setName("my-thread");
// 运行线程
thread.start();
}
}

使用继承方式的好处是,在run()方法内获取当前线程直接使用this就可以了,无须使用Thread.currentThread()方法;不好的地方是Java不支持多继承,如果继承了Thread类,那么就不能再继承其他类。另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码

2 实现 Runnable 接口

实现Runnable类,并实现其run()方法,也可以创建一个线程。

1
2
3
4
5
6
7
8
9
10
11
public class ImplRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "is running...");
}

public static void main(String[] args) {
ImplRunnable thread = new ImplRunnable();
new Thread(thread, "my-thread").start();
}
}

Runnable接口是一个被@FunctionalInterface注解修饰,因此可以通过lambda表达式进行创建,因此使用Runnable方式创建线程也可以通过下列方式进行简化:

1
2
3
4
5
6
public class ImplRunnableLambda {
public static void main(String[] args) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "is running...");
}, "my-thread").start();
}

3 实现 Callable 接口,并结合 Future 实现

首先,要定义一个Callable实现类,并实现call方法;其次,通过Future的构造方法传入Callable实现类的实例;然后,把FutureTask作为Thread类的 target ,创建 Thread 线程对象;最后,可以通过Futureget方法获取线程的运行结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UseFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new ImplCallable());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get()); // 获得线程运行后的返回值,阻塞式
}
}

class ImplCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "thread execute finished";
}
}

总结

  • 相比于第一种方式,更推荐第二种方式。因为继承继承Thread类往往不符合里氏代换原则,而实现Runnable接口可以使编程更加灵活,对外暴露的细节较少,使用者只需要关注run()方法的实现上;

  • RunnableCallable接口的定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Runnable接口
    @FunctionalInterface
    public interface Runnable {
    public abstract void run();
    }

    // Callable接口
    @FunctionalInterface
    public interface Callable<V> {
    V call() throws Exception;
    }

    通过对比两个接口定义可知RunnableCallable有两点不同:(1)通过call方法可以获取返回值。前两种方式在任务结束后,无法直接获取执行结果,只能通过共享变量获取,而第三种则可解决这一问题;(2)call可以抛出要异常,Runnable则需要通过setDefaultUncaughtExceptionHandler()方法才能在主线程中获取子线程中的异常。

每创建一个线程,实际上会在虚拟机栈上创建一个新的栈。因为每个虚拟机栈都是线程私有的,因此各线程之间不会相互干扰。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!