Java 线程Thread和Runnable教程

x33g5p2x  于2021-10-16 转载在 Java  
字(5.3k)|赞(0)|评价(0)|浏览(229)

这是我的 Java 并发系列教程的第二部分。在第一部分,我们学习了并发、进程和线程的基础知识。在这篇文章中,我们将学习如何创建新线程并在这些线程中运行任务。

创建和启动线程

在 Java 中有两种创建线程的方法 -

1.通过扩展Thread类

你可以简单地通过从 Thread 扩展你的类并覆盖它的 run() 方法来创建一个新线程。

run() 方法包含在新线程内执行的代码。创建线程后,您可以通过调用 start() 方法启动它。

public class ThreadExample extends Thread {

    // run() method contains the code that is executed by the thread.
    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating thread...");
        Thread thread = new ThreadExample();

        System.out.println("Starting thread...");
        thread.start();
    }
}
# Output
Inside : main
Creating thread...
Starting thread...
Inside : Thread-0

Thread.currentThread() 返回对当前正在执行的线程的引用。在上面的例子中,我使用了线程的 getName() 方法来打印当前线程的名称。

每个线程都有一个名称。您可以使用 Thread(String name) 构造函数创建具有自定义名称的线程。如果未指定名称,则会自动为线程选择一个新名称。

2. 通过提供一个 Runnable 对象

Runnable 接口是任何打算由线程执行的对象的主要模板。它定义了一个单独的方法 run(),该方法旨在包含由线程执行的代码。

任何其实例需要由线程执行的类都应该实现 Runnable 接口。

Thread 类本身实现了 Runnable,而 run() 方法的实现是空的。

要创建新线程,请创建实现 Runnable 接口的类的实例,然后将该实例传递给 Thread(Runnable target) 构造函数。

public class RunnableExample implements Runnable {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new RunnableExample();

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("Inside : " + Thread.currentThread().getName());
    }
}
# Output
Inside : main
Creating Runnable...
Creating Thread...
Starting Thread...
Inside : Thread-0

请注意,您可以使用 Java 的 anonymous class 语法创建匿名可运行对象,而不是创建一个实现 Runnable 的类,然后实例化该类以获取可运行对象。

匿名类使您能够使您的代码更简洁。它们使您能够同时声明和实例化一个类。 - 来自 Java 文档。

public class RunnableExampleAnonymousClass {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Inside : " + Thread.currentThread().getName());
            }
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();
    }
}

上面的例子可以通过使用 Java 8 的 lambda 表达式变得更短——

public class RunnableExampleLambdaExpression {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Runnable...");
        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());
        };

        System.out.println("Creating Thread...");
        Thread thread = new Thread(runnable);

        System.out.println("Starting Thread...");
        thread.start();

    }
}

Runnable 或 Thread,使用哪一个?

第一种方法,通过从 Thread 类扩展来创建线程是非常有限的,因为一旦从 Thread 扩展类,就不能从任何其他类扩展,因为 Java 不允许多个遗产。

此外,如果您遵循良好的设计实践,继承旨在扩展父类的功能,但是当您创建线程时,您不会扩展 Thread 类的功能,您只是提供了run() 方法。

因此,通常,您应该始终使用 Runnable 对象来创建线程。这种方法比较灵活。它允许您的类从任何其他类扩展。此外,您可以在 Runnable 中使用匿名类语法和 Java 8 的 lambda 表达式,使您的代码更加简洁。

使用 sleep() 暂停线程的执行

Thread 类提供的 sleep() 方法允许您将当前正在执行的线程的执行暂停指定的毫秒数。

public class ThreadSleepExample {

    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        String[] messages = {"If I can stop one heart from breaking,",
                "I shall not live in vain.",
                "If I can ease one life the aching,",
                "Or cool one pain,",
                "Or help one fainting robin",
                "Unto his nest again,",
                "I shall not live in vain"};

        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());

            for(String message: messages) {
                System.out.println(message);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new IllegalStateException(e);
                }
            }
        };

        Thread thread = new Thread(runnable);

        thread.start();
    }
}
# Output
Inside : main
Inside : Thread-0
If I can stop one heart from breaking,
I shall not live in vain.
If I can ease one life the aching,
Or cool one pain,
Or help one fainting robin
Unto his nest again,
I shall not live in vain

上面的例子包含一个 for 循环,它遍历消息数组,打印当前消息,通过调用 Thread.sleep() 等待 2 秒,然后继续下一次迭代。

sleep() 方法抛出 InterruptedException 如果任何线程中断当前线程。 InterruptedException 是一个检查异常,必须处理。

使用 join() 等待另一个线程完成

join() 方法允许一个线程等待另一个线程的完成。下面的例子中,线程2通过调用Thread.join(1000)等待线程1完成1000毫秒,然后开始执行——

public class ThreadJoinExample {

    public static void main(String[] args) {
        // Create Thread 1
        Thread thread1 = new Thread(() -> {
            System.out.println("Entered Thread 1");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 1");
        });

        // Create Thread 2
        Thread thread2 = new Thread(() -> {
            System.out.println("Entered Thread 2");
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            System.out.println("Exiting Thread 2");
        });

        System.out.println("Starting Thread 1");
        thread1.start();

        System.out.println("Waiting for Thread 1 to complete");
        try {
            thread1.join(1000);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }

        System.out.println("Waited enough! Starting Thread 2 now");
        thread2.start();
    }
}
Starting Thread 1
Waiting for Thread 1 to complete
Entered Thread 1
Waited enough! Starting Thread 2 now
Entered Thread 2
Exiting Thread 1
Exiting Thread 2

Thread.join() 的等待时间等于 MIN(线程终止所用的时间,方法参数中指定的毫秒数)。

join() 方法也可以不带参数调用。在这种情况下,它只是等待线程死亡。

结论

在本教程中,我们学习了在 Java 应用程序中创建线程的两种方法。我们还了解了 Thread 的 sleep()join() 方法。本教程中使用的所有代码片段都可以在 my github repository 中找到。

相关文章