Java线程对象

每个线程都关联了一个Thread实例。使用线程对象创建并发应用,有两种基本策略:

  • 直接控制线程的创建和管理,每当应用程序需要启动一个异步任务时,简单地实例化Thread。
  • 将线程管理从应用程序的其余部分抽象出来,把应用程序的任务传给executor。

本文仅介绍Thread对象的使用。Executor在Java高级并发对象中讨论。

定义并启动线程

创建Thread实例的应用必须提供在线程中运行的代码。有两种方式可以做到:

  • 提供一个Runnable对象。Runnable接口定义了单个方法,run,用来包含线程执行代码。就像HelloRunnable一例中那样,把Runnable对象传递给Thread的构造方法:

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对象的第一种方式更加通用,因为不需要继承Thread类。第二种方式在简单的应用程序中使用起来更容易,但是要求任务类必须继承于Thread。本文将重点关注第一种方式,它将Runnable任务从执行任务的Thread对象中分离出来。这种方式不仅更加灵活,而且适用于之后会讲到的高级线程管理API。

Thread类定义了许多在线程管理方面非常有用的方法。其中包含了一些静态方法,它们能够提供调用方法的线程的信息或影响其状态。另外的涉及管理线程以及Thread对象的方法则在其他线程中调用。

用sleep暂停线程的执行

Thread.sleep使得当前线程在某一指定周期内暂停执行。这是一种让出处理器时间的有效方法。Something。。。

sleep方法有两个重载版本:一个指定睡眠时间到毫秒,一个指定睡眠时间到纳秒。然而,睡眠时间并不保证精确,因为这受限于底层系统设施。而且,睡眠周期可以被中断终止。无论如何,你不能假定调用sleep会使线程精确地暂停一段指定的时间。

SleepMessages使用sleep每隔4秒钟打印信息:

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声明了throws InterruptedException。如果当前线程在sleep期间被其他线程中断,那么sleep方法就会抛出该异常。由于这个程序没有定义会引起中断的其他线程,所以无需关心捕获InterruptedException。

中断

中断就是指示线程停止当前任务,去做其他事情。线程如何响应中断取决于程序员,但是通常的做法是终止线程。这是本文强调的做法。

要中断一个线程,在该线程关联的Thread对象上调用interrupt方法。为了让中断机制正确地运作,被中断的线程必须支持中断。

支持中断

线程如何支持中断呢?这取决于它当前的任务。如果线程频繁地调用抛出InterruptedException的方法,那么它只需在捕获到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。

加入

join方法允许一个线程等待另一个线程的完成。如果t是一个正在执行的Thread对象,

t.join();

使得当前线程暂停执行,直到t结束。join的重载版本允许程序员指定一个等待周期。然而,和sleep一样,join的定时依赖于操作系统,所以你不能假定join等待的时间恰好是你指定的时间。

和sleep一样,join在接收到中断时抛出InterruptedException并退出。

简单的线程实例

下面的例子集合了之前讲到的某些概念。SimpleThreads由两个线程组成。第一个是每个Java程序都会有的主线程。主线程从一个Runnable对象MessageLoop创建一个新线程,并等待其结束。如果MessageLoop线程长时间不结束,主线程将中断它。

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!");
    }
}

本文内容出自JavaSE Tutorial


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值