Oracle官方并发教程之线程对象

简介:

原文链接译文链接,译者:郑旭东

在Java中,每个线程都是Thread类的实例。并发应用中一般有两种不同的线程创建策略。

  • 直接控制线程的创建和管理,每当应用程序需要执行一个异步任务的时候就为其创建一个线程
  • 将线程的管理从应用程序中抽象出来作为执行器,应用程序将任务传递给执行器,有执行器负责执行。

这一节,我们将讨论Thread对象,有关Executors将在高级并发对象一节中讨论。

定义并启动一个线程

应用程序在创建一个线程实例时,必须提供需要在线程中运行的代码。有两种方式去做到这一点:

  • 提供一个Runnable对象。Runnable对象仅包含一个run()方法,在这个方法中定义的代码将在会线程中执行。将Runnable对象传递给Thread类的构造函数即可,如下面这个HelloRunnable的例子:
public class HelloRunnable implements Runnable {

    public void run() {
        System.out.println("Hello from a thread!");
    }

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

}
  • 继承Thread类。Thread类自身已实现了Runnable接口,但它的run()方法中并没有定义任何代码。应用程序可以继承与Thread类,并复写run()方法。如例子HelloThread
public class HelloThread extends Thread {

    public void run() {
        System.out.println("Hello from a thread!");
    }

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

}

需要注意的是,上述两个例子都需要调用Thread.start()方法来启动一个新的线程。 哪一种方式是我们应该使用的?相对来说,第一种更加通用,因为Runnable对象可以继承于其他类(Java只支持单继承,当一个类继承与Thread类后,就无法继承与其他类)。第二种方法更易于在简单的应用程序中使用,但它的局限就是:你的任务类必须是Thread的子类。这个课程更加聚焦于第一种将Runnable任务和Thread类分离的方式。不仅仅是因为这种方式更加灵活,更因为它更适合后面将要介绍的高级线程管理API。 Thread类定义了一些对线程管理十分有用的的方法。在这些方法中,有一些静态方法可以给当前线程调用,它们可以提供一些有关线程的信息,或者影响线程的状态。而其他一些方法可以由其他线程进行调用,用于管理线程和Thread对象。我们将在下面的章节中,深入探讨这些内容。

使用Sleep方法暂停一个线程

使用Thread.sleep()方法可以暂停当前线程一段时间。这是一种使处理器时间可以被其他线程或者运用程序使用的有效方式。sleep()方法还可以用于调整线程执行节奏(见下面的例子)和等待其他有执行时间需求的线程(这个例子将在下一节演示)。

在Thread中有两个不同的sleep()方法,一个使用毫秒表示休眠的时间,而另一个是用纳秒。由于操作系统的限制休眠时间并不能保证十分精确。休眠周期可以被interrups所终止,我们将在后面看到这样的例子。不管在任何情况下,我们都不应该假定调用了sleep()方法就可以将一个线程暂停一个十分精确的时间周期。

SleepMessages程序为我们展示了使用sleep()方法每四秒打印一个信息的例子

public class SleepMessages {
    public static void main(String args[])
        throws InterruptedException {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };

        for (int i = 0;
             i < importantInfo.length;
             i++) {
            //Pause for 4 seconds
            Thread.sleep(4000);
            //Print a message
            System.out.println(importantInfo[i]);
        }
    }
}

main()方法声明了它有可能抛出InterruptedException。当其他线程中断当前线程时,sleep()方法就会抛出该异常。由于这个应用程序并没有定义其他的线程,所以并不用关心如何处理该异常。

中断(Interrupts)

原文链接译文链接译者: 蘑菇街-小宝   校对:梁海舰
中断是给线程的一个指示,告诉它应该停止正在做的事并去做其他事情。一个线程究竟要怎么响应中断请求取决于程序员,不过让其终止是很普遍的做法。这是本文重点强调的用法。

一个线程通过调用对被中断线程的Thread对象的interrupt()方法,发送中断信号。为了让中断机制正常工作,被中断的线程必须支持它自己的中断(即要自己处理中断)

中断支持

线程如何支持自身的中断?这取决于它当前正在做什么。如果线程正在频繁调用会抛InterruptedException异常的方法,在捕获异常之后,它只是从run()方法中返回。例如,假设在SleepMessages的例子中,关键的消息循环在线程的Runnable对象的run方法中,代码可能会被修改成下面这样以支持中断:

for (int i = 0; i < importantInfo.length; i++) {
    // Pause for 4 seconds
    try {
       Thread.sleep(4000);
    } catch (InterruptedException e) {
       // We've been interrupted: no more messages.
      return;
 }
 // Print a message
 System.out.println(importantInfo[i]);
}

许多会抛InterruptedException异常的方法(如sleep()),被设计成接收到中断后取消它们当前的操作,并在立即返回。

如果一个线程长时间运行而不调用会抛InterruptedException异常的方法会怎样? 那它必须周期性地调用Thread.interrupted()方法,该方法在接收到中断请求后返回true。例如:

for (int i = 0; i < inputs.length; i++) {
    heavyCrunch(inputs[i]);
    if (Thread.interrupted()) {
        // We've been interrupted: no more crunching.
        return;
    }
}

在这个简单的例子中,代码只是检测中断,并在收到中断后退出线程。在更复杂的应用中,抛出一个InterruptedException异常可能更有意义。

if (Thread.interrupted()){
   throw new InterruptedException();
}

这使得中断处理代码能集中在catch语句中。

中断状态标记

中断机制通过使用称为中断状态的内部标记来实现。调用Thread.interrupt()设置这个标记。当线程通过调用静态方法Thread.interrupted()检测中断时,中断状态会被清除。非静态的isInterrupted()方法被线程用来检测其他线程的中断状态,不改变中断状态标记。

按照惯例,任何通过抛出一个InterruptedException异常退出的方法,当抛该异常时会清除中断状态。不过,通过其他的线程调用interrupt()方法,中断状态总是有可能会立即被重新设置。

Joins

Join()方法可以让一个线程等待另一个线程执行完成。若t是一个正在执行的Thread对象,

 t.join();

将会使当前线程暂停执行并等待t执行完成。重载的join()方法可以让开发者自定义等待周期。然而,和sleep()方法一样join()方法依赖于操作系统的时间处理机制,你不能假定join()方法将会精确的等待你所定义的时长。

如同sleep()方法,join()方法响应中断并在中断时抛出InterruptedException。

一个简单的线程例子

下面这个简单的例子将会把这一节的一些概念放到一起演示。SimpleThreads程序有两个线程组成,第一个是主线程,它从创建了一个线程并等待它执行完成。如果MessageLoop线程执行了太长时间,主线程将会将其中断。

MessageLoop现场将会打印一系列的信息。如果中断在它打印完所有信息前发生,它将会打印一个特定的消息并退出。

public class SimpleThreads {

    // Display a message, preceded by
    // the name of the current thread
    static void threadMessage(String message) {
        String threadName =
            Thread.currentThread().getName();
        System.out.format("%s: %s%n",
                          threadName,
                          message);
    }

    private static class MessageLoop
        implements Runnable {
        public void run() {
            String importantInfo[] = {
                "Mares eat oats",
                "Does eat oats",
                "Little lambs eat ivy",
                "A kid will eat ivy too"
            };
            try {
                for (int i = 0;
                     i < importantInfo.length;
                     i++) {
                    // Pause for 4 seconds
                    Thread.sleep(4000);
                    // Print a message
                    threadMessage(importantInfo[i]);
                }
            } catch (InterruptedException e) {
                threadMessage("I wasn't done!");
            }
        }
    }

    public static void main(String args[])
        throws InterruptedException {

        // Delay, in milliseconds before
        // we interrupt MessageLoop
        // thread (default one hour).
        long patience = 1000 * 60 * 60;

        // If command line argument
        // present, gives patience
        // in seconds.
        if (args.length > 0) {
            try {
                patience = Long.parseLong(args[0]) * 1000;
            } catch (NumberFormatException e) {
                System.err.println("Argument must be an integer.");
                System.exit(1);
            }
        }

        threadMessage("Starting MessageLoop thread");
        long startTime = System.currentTimeMillis();
        Thread t = new Thread(new MessageLoop());
        t.start();

        threadMessage("Waiting for MessageLoop thread to finish");
        // loop until MessageLoop
        // thread exits
        while (t.isAlive()) {
            threadMessage("Still waiting...");
            // Wait maximum of 1 second
            // for MessageLoop thread
            // to finish.
            t.join(1000);
            if (((System.currentTimeMillis() - startTime) > patience)
                  && t.isAlive()) {
                threadMessage("Tired of waiting!");
                t.interrupt();
                // Shouldn't be long now
                // -- wait indefinitely
                t.join();
            }
        }
        threadMessage("Finally!");
    }
}
文章转自  并发编程网-ifeve.com
目录
相关文章
|
2月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
234 0
|
1月前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
198 59
|
1月前
|
安全 Java
线程安全的艺术:确保并发程序的正确性
在多线程环境中,确保线程安全是编程中的一个核心挑战。线程安全问题可能导致数据不一致、程序崩溃甚至安全漏洞。本文将分享如何确保线程安全,探讨不同的技术策略和最佳实践。
41 6
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
58 6
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
2月前
|
Oracle 关系型数据库 网络安全
Oracle 19c 安装教程学习
Oracle 19c 安装教程学习
92 2
|
2月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
36 1
|
3月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
77 0
|
5月前
|
SQL Oracle 关系型数据库
MySQL、SQL Server和Oracle数据库安装部署教程
数据库的安装部署教程因不同的数据库管理系统(DBMS)而异,以下将以MySQL、SQL Server和Oracle为例,分别概述其安装部署的基本步骤。请注意,由于软件版本和操作系统的不同,具体步骤可能会有所变化。
387 3