Java创建线程的3种方式(非常详细,附带实例)
在 Java 中创建线程有 3 种方式,分别是:
- 通过定义 Thread 类的子类创建线程;
 - 通过定义 Runnable 接口的实现类创建线程;
 - 通过 Callable 接口和 Future 接口创建线程。
 
1、继承Thread类创建线程
Thread 是 Java 中代表线程的类,位于 java.lang 包中。Thread 类中包含封装线程具体信息的属性(如线程的名称、id 和优先级等),以及对线程进行操作的常用方法(如线程休眠和唤醒等)。在 Java 中,每个 Thread 类的对象代表一个具体的线程。
Thread 类常用的构造方法如下:
- public Thread():分配一个新的线程对象;
 - public Thread(String name):分配一个指定名称的新的线程对象;
 - public Thread(Runnable target):分配一个带指定目标的新的线程对象;
 - public Thread(Runnable target, String name):分配一个带指定目标的且指定名称的新的线程对象。
 
Thread 类其他常用的方法如下:
- public String getName():获取当前线程名称;
 - public void start():此线程开始执行,Java 虚拟机调用此线程的 run() 方法;
 - public void run():此线程要执行的任务在此处定义代码;
 - public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行);
 - public static Thread currentThread():返回对当前正在执行的线程对象的引用。
 
使用 Thread 类实现线程的步骤如下:
- 定义 Thread 类的子类并重写该类的 run() 方法,该方法的方法体代表该线程需要完成的任务;
 - 创建 Thread 类的实例,即创建线程对象;
 - 调用线程的 start() 方法来启动线程。
 
【实例】通过继承 Thread 类来创建线程。
public class Demo {
    public static void main(String[] args) {
        // 基于自定义线程类创建线程对象
        MyThread t1 = new MyThread("Thread1");
        // 启动线程
        t1.start();
        MyThread t2 = new MyThread("Thread2");
        t2.start();
    }
}
/**
* 自定义线程类
*/
class MyThread extends Thread {
    public MyThread(String name) {
        // 调用父类的构造方法,初始化线程名称
        super(name);
        // 调用父类的getName()方法,获取线程名称
        System.out.println("Creating " + this.getName());
    }
    @Override
    public void run() {
        System.out.println("Running " + this.getName());
        try {
            for (int i = 3; i > 0; i--) {
                System.out.println("Thread: " + this.getName() + ", " + i);
                // 让线程睡眠一段时间
                Thread.sleep(50);
            }
        } catch (InterruptedException e) {
            System.out.println("Thread " + this.getName() + " interrupted.");
        }
        System.out.println("Thread " + this.getName() + " exiting.");
    }
    @Override
    public void start() {
        System.out.println("Starting " + this.getName());
        // 调用Thread类的start()方法,启动线程
        super.start();
    }
}
运行结果为:
	Creating Thread1
	Starting Thread1
	Creating Thread2
	Starting Thread2
	Running Thread1
	Thread: Thread1, 3
	Running Thread2
	Thread: Thread2, 3
	Thread: Thread2, 2
	Thread: Thread1, 2
	Thread: Thread2, 1
	Thread: Thread1, 1
	Thread Thread2 exiting.
	Thread Thread1 exiting.
2、实现Runnable接口创建线程
Runnable 接口用来封装一个线程启动后所要执行的具体逻辑。在 Java 中,任何打算由线程执行的类,都应该实现 Runnable 接口,并提供其中定义的抽象方法 run() 的具体逻辑。实际上,Thread 类也实现了 Runnable 接口。Runnable 接口的设计,实现了线程管理和线程执行逻辑的分离。Thread 类负责线程属性的封装和线程状态的管理。Runnable 接口的实现类负责提供线程的具体执行逻辑。
使用 Runnable 接口创建线程的步骤如下:
- 定义 Runnable 接口的实现类,并且重写它的 run() 方法,这个方法同样是该线程的执行体;
 - 创建 Runnable 实现类的实例,并且将此实例作为 Thread 类的 target 创建一个 Thread 对象,该对象才是真正的线程对象;
 - 调用 start() 方法启动该线程。
 
【实例】使用 Runnable 接口创建线程类。
public class Demo {
    public static void main(String[] args) {
        // 创建Runnable接口实现类的对象
        MyRunner myRunner = new MyRunner();
        // 创建子线程对象,绑定Runnable接口实现类的对象
        Thread t1 = new Thread(myRunner, "Thread1");
        Thread t2 = new Thread(myRunner, "Thread2");
        // 启动子线程对象
        t1.start();
        t2.start();
    }
}
class MyRunner implements Runnable {
    @Override
    public void run() {
        // 获取当前正在执行的线程对象的名称
        String threadName = Thread.currentThread().getName();
        System.out.println("Running " + threadName);
        try {
            for (int i = 3; i > 0; i--) {
               System.out.println("Thread: " + threadName + ", " + i);
               // 让线程睡眠一段时间
               Thread.sleep(50);
           }
        } catch (InterruptedException e) {
            System.out.println("Thread " + threadName + " interrupted.");
        }
        System.out.println("Thread " + threadName + " exiting.");
    }
}
运行结果为:
	Running Thread1
	Thread: Thread1, 3
	Running Thread2
	Thread: Thread2, 3
	Thread: Thread1, 2
	Thread: Thread2, 2
	Thread: Thread1, 1
	Thread: Thread2, 1
	Thread Thread2 exiting.
	Thread Thread1 exiting.
3、Callable和Future接口创建线程
通过 Thread 类和 Runnable 接口创建多线程,需要重写 run() 方法,但该方法没有返回值,因此无法从多个线程中获取返回结果。Java 提供了一个 Callable 接口,既可以通过该接口创建多线程,又可以提供返回值。Callable 是 JDK 1.5 推出的接口,与 Runnable 接口相似,其中只有一个方法 call()。call() 方法可以抛出 Exception 类及其子类的异常,并且可以返回指定的泛型类对象。
FutureTask 类代表一个可取消的异步计算任务。FutureTask 是 Future 接口的基础实现类,包含启动异步和取消计算的方法、查看计算任务是否完成的方法和查询计算结果的方法。只有在计算完成后才能检索结果,如果计算尚未完成,那么 get() 方法将被阻塞。一旦计算完成,就不能重新开始或取消计算(除非使用 runAndReset 调用计算)。
通过 Callable 接口和 Future 接口创建线程的步骤如下:
- 创建 Callable 接口的实现类,同时实现 call() 方法,该方法将作为线程执行体,并且有返回值;
 - 创建 Callable 接口的实现类的实例,使用 FutureTask 类来包装 Callable 对象,FutureTask 对象封装了 Callable 对象的 call() 方法的返回值;
 - 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程;
 - 调用 FutureTask 对象的 get() 方法获得子线程执行结束后的返回值。
 
【实例】使用 Callable 接口创建线程。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Demo {
    public static void main(String[] args) {
        CallableDemo cd = new CallableDemo();
        FutureTask<Integer> ft = new FutureTask<>(cd);
        new Thread(ft, "有返回值的线程").start();
       
        try {
           System.out.println("子线程的返回值:" + ft.get());
       } catch (InterruptedException e) {
           e.printStackTrace();
        } catch (Exception e) {
           e.printStackTrace();
        }
    }
}
class CallableDemo implements Callable<Integer> {
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
        return i;
    }
}
运行结果为:
	有返回值的线程 0
	有返回值的线程 1
	有返回值的线程 2
	子线程的返回值:3
3种线程创建方式的对比
当采用继承 Thread 类的方式创建线程类时,受 Java 单重继承机制的影响,该线程类无法继承其他类,可能对该线程类的扩展性造成影响。当使用 Runnable 接口和 Callable 接口创建线程类时,线程类不仅实现了 Runnable 接口和 Callable 接口,还可以继承其他类,扩展性更好。
在大多数情况下,如果只打算覆盖 run() 方法而不打算覆盖 Thread 类其他的方法,那么应该使用 Runnable 接口。这很重要,因为除非开发者打算修改或增强类的基本行为,否则不应将类子类化。
当采用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,就无须使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
 ICP备案:
 公安联网备案: