首页 > 编程笔记 > Java笔记 阅读:3

java创建线程的三种方法(非常详细,附带示例)

在 Java 程序中,创建线程的方法有种,分别是:
  1. 继承 Thread 类,创建线程并启动线程;
  2. 实现 Runnable 接口,创建线程并启动线程;
  3. 使用匿名内部类对象,实现对线程的创建和启动。

接下来将展开讲解这种方法。

继承Thread类

Java 通过继承 Thread 类来创建并启动多线程,下面是具体的实现流程:
  1. 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法的方法体代表线程需要完成的任务;
  2. 创建 Thread 子类的实例,即创建线程对象;
  3. 调用线程对象的 start() 方法来启动该线程。

【实例】继承 Thread 类,创建线程并启动线程,代码如下:
/**
* 自定义线程继承 Thread 类
*/
public class MyThreadDemo extends Thread {
    // 定义指定线程名称的构造方法
    public MyThreadDemo(String name) {
        // 调用父类的 String 参数的构造方法,指定线程的名称
        super(name);
    }

    /**
     * 重写 run 方法,实现该线程执行的逻辑
     */
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + ":正在执行!" + i);
        }
    }
}

public class ThreadDemoTest {
    public static void main(String[] args) {
        // 创建自定义线程对象1
        MyThreadDemo myThreadDemo = new MyThreadDemo("线程 1");
        // 启动线程1
        myThreadDemo.start();

        // 创建自定义线程对象2
        MyThreadDemo myThreadDemo02 = new MyThreadDemo("线程 2");
        // 启动线程2
        myThreadDemo02.start();

        // 在 main 方法中执行 for 循环
        for (int i = 1; i <= 10; i++) {
            System.out.println("main 线程:" + i);
        }
    }
}
在上述代码中,MyThreadDemo 类通过继承 Thread 类实现了对自定义线程的创建。

MyThreadDemo 类重写了 Thread 类的 run() 方法,该方法定义了线程被调度执行时所需完成的任务。而在 ThreadDemoTest 类中,为了启动这个自定义线程,必须调用其 start() 方法。

采用继承 Thread 类创建线程的方法,需要注意以下几点:
  1. 如果手动调用 run() 方法,那么它就是一个普通的 Java 方法,并不会启动多线程模式。
  2. run() 方法通常由 JVM 在特定时刻内调用,而调用的时机以及对执行过程的控制,完全由操作系统对 CPU 的调度决定。
  3. 启动多线程,必须调用线程的 start() 方法。
  4. 一个线程对象只能被调用一次 start() 方法来启动,若尝试重复调用,则会抛出 IllegalThreadStateException 异常。
  5. 线程启动流程,如下图所示:


图 1 线程启动流程

实现Runnable接口

Java 具有单继承限制,当无法直接继承 Thread 类时,应该如何应对?

Java 核心类库提供 Runnable 接口,这一机制允许通过实现 Runnable 接口并重写其 run() 方法来定义线程的执行体。随后,我们可以用 Thread 类的对象作为代理,启动并执行自定义的 run() 方法,从而灵活地实现多线程编程。

下面是实现 Runnable 接口的具体流程:
  1. 定义 Runnable 接口的实现类,并重新实现该接口的 run() 方法。该 run() 方法的具体实现即该线程的线程执行体。
  2. 创建 Runnable 接口实现类的实例,并将该实例作为 Thread 类的 target 参数来构造 Thread 对象。这个 Thread 对象即真正的线程实体。
  3. 通过调用线程对象的 start() 方法来启动线程。注意,不应该直接调用 Runnable 接口实现类的 run() 方法,因为这样做并不会启动一个新的线程,而是像调用普通方法一样在当前线程中执行 run() 方法的内容。

【实例】实现 Runnable 接口,创建线程并启动线程,代码如下:
public class MyThreadDemo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

public class RunnableDemoTest {
    public static void main(String[] args) {
        // 创建自定义的对象,线程任务对象
        MyThreadDemo myThreadDemo02 = new MyThreadDemo();
        // 创建线程对象
        Thread thread = new Thread(myThreadDemo02);
        // 启动线程对象
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("chapter13: " + i);
        }
    }
}
在上述代码中,MyThreadDemo 类通过实现 Runnable 接口,并覆写(重写)其 run() 方法来定义线程的执行任务。

重要的是,MyThreadDemo 并非直接代表线程对象本身,而是作为线程任务的一个载体或对象存在。实际的线程对象需要在 RunnableDemoTest 类中通过适当的构造进行创建,随后通过调用 start() 方法来启动这一线程,从而执行在 MyThreadDemo 类中定义的 run() 方法内的任务。

实现 Runnable 接口避免了单继承的局限性,多个线程可以共享同一个接口实现类的对象,非常适合多个相同的线程来使用同一资源的情况。多线程可以增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

实现 Runnable 接口创建线程,需要注意以下几点:
  1. 通过实现 Runnable 接口,相应的类便具备了多线程特性。所有需要在分线程中执行的代码都应当被置于 run() 方法之内。
  2. 在启动多线程时,需要利用 Thread 类的构造方法 Thread(Runnable target) 来创建一个 Thread 对象,并指定 Runnable 接口的实现类作为参数。随后,通过调用该 Thread 对象的 start() 方法来启动并执行多线程代码。
  3. 实际上,无论是继承 Thread 类,还是实现 Runnable 接口,最终都是依赖 Thread 对象的 API 来控制线程的。因此,熟悉 Thread 类的 API 是进行多线程编程不可或缺的基础。
  4. Runnable 对象在这里仅作为 Thread 对象的 target 参数存在,而 Runnable 接口实现类中的 run() 方法则充当了线程的执行体。尽管实际的线程对象是 Thread 的实例,但正是这个 Thread 线程负责执行其 target 参数指定的 run() 方法中的代码。

匿名内部类创建启动线程

我们使用匿名内部类对象,实现对线程的创建和启动。

【实例】使用匿名内部类对象创建线程并启动线程,代码如下:
public class AnonymousDemoTest {
    public static void main(String[] args) {
        AnonymousDemoTest anonymousDemoTest = new AnonymousDemoTest();
        anonymousDemoTest.testThread();
        anonymousDemoTest.testRunn();
    }

    /**
     * 使用匿名内部类创建线程类,调用 start() 方法,启动线程
     */
    public void testThread() {
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(getName() + ":正在执行!" + i);
                }
            }
        }.start();
    }

    /**
     * 使用匿名内部类创建线程类,调用 start() 方法,启动线程
     */
    public void testRunn() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        }).start();
    }
}
在 testThread() 方法中,我们直接运用了 Thread 类来实例化一个线程对象,通过重写其 run() 方法来定义线程的执行任务,随后调用 start() 方法来启动线程的执行。

而在 testRunn() 方法中,创建线程的方式依赖实现 Runnable 接口的类,我们同样需要重写 run() 方法来指定线程的任务内容,但启动线程的方式稍有不同。这里是通过 Thread 类的构造器传入实现 Runnable 接口的实例,并调用其 start() 方法来启动线程。

相关文章