java并发(二、线程对象)

本文介绍了Java线程的基础知识,包括线程对象的创建与管理、线程的暂停与中断、线程间的等待与唤醒等核心概念,并通过示例代码演示了如何在实际应用中运用这些技术。

、线程对象

每个线程是类 Thread的一个实例。有两个使用线程对象创建一个并发应用的策略。

直接创建和管理线程,每次应用需要开始一个异步任务时,简单的实例化Thread

从其余的应用抽象出线程管理,把应用任务传给executor。

这一段使用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的实现对象,更常用,因为Runnable实现对象能,因为Runnable实现对象还可以继承一个父类。第二种实现在简单应用中是简单的,但是必须是Thread的子类,这实际上是由限制的。这里主要集中在第一种方法,脱离Thread对象执行任务。这种方法不仅更灵活,而且适用于下面的高级线程管理。

Thread类定义了一些对线程管理有用的方法。包括提供相关信息的静态方法和影响线程状态的方法。还有用来管理线程和线程对象的被其他线程调用的方法。我们将在下面学些这些方法。

(二)           用sleep 暂停一个线程

Thread.sleep使线程在指定一段时间内暂停。这是一个有效的方法使处理器去处理其他线程或者其他应用。Sleep也可以用来步调(pacing),如下所示,和等待另一个线程的执行要求如下SimpleThreads例子。

提供了两个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方法声明抛出InterruptedException。这是当前线程sleep时被其他线程中断抛出的异常。因为这个应用没有定义另一个线程引起中断,所以不必费心去捕获。

(三)           中断

中断是指一个线程正在做的事应该停止。它适用于程序员去正确的决定线程应该如何响应中断。但是通常情况是终止线程。这是这章强调的用法。

线程通过调用Thread对象中的interrupt方法发送interrupt使线程中断。为了使中断机制正确工作,中断的线程必须支持它自己的中断。

1.         支持中断

一个线程怎样支持自己中断呢?这个依 赖它当前在做什么。如果线程在调用方法时频繁的抛出异常,它会在捕获异常后从这个run方法中返回。比如,假设在SleepMessages例子中主要的 for循环是在一个线程的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块里

2.         中断状态标识

中断机制是使用一个称作中断状态的内部标识来实现的。调用Thread.interrupt设置这个标识。当线程通过调用静态方法 Thread.interrupted检查中断时,中断状态会被清除。非静态的isInterrupted方法被用作一个线程查询另外一个线程的中断状 态,这个不会改变中断状态标识。

按照惯例,任何方法通过抛出异常退出会清除中断状态。然而,极可能中断状态会因为其他的线程调用interrupt而立即被再次设置。

(四)           Joins

      join方法允许一个线程等得另个线程的完成。如果t是当前执行的一个线程对象,
       t.join()
引起当前线程暂停,直到t线程终止。join的重载允许程序员指定一个等待时间。然而,和sleep一样,join的等待时间也是不精确的,所以你不要假设join方法会按照你指定的时间精确执行。

      像sleep一样,join抛出一个InterruptedException响应中断请求。

(五)           一个简单的线程例子

下面的例子展示了这章的内容。SimpleThreads包含两个线程。第一个是每个java程序都有的main线程。Main线程通过Tunnable创建一个子线程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!");

    }

}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值