美文网首页
【The Java™ Tutorials】【Concurrenc

【The Java™ Tutorials】【Concurrenc

作者: Ppian | 来源:发表于2018-04-19 14:16 被阅读0次

Java中每个线程都和一个Thread实例关联。使用线程对象创建一个并发应用有两种策略:

  • 直接控制线程的创建和管理,每次应用要执行一个异步任务时,就实例化一个Thread
  • 在应用中抽象出线程管理,然后传递应用的任务给executor去执行

这一章只介绍Threadexecutor放在后面的章节介绍。

定义和开始一个线程

一个应用实例化一个Thread的时候必须要提供这个线程要执行的代码。有以下两种方式:

  • 提供一个Runnable对象。Runnable接口里面只有一个方法run,要执行的代码在放在该方法里面。将该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();
    }

}
  • 继承ThreadThread本身实现了Runnable接口,只是默认没有执行任何代码。我们只要重写run方法:
public class HelloThread extends Thread {

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

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

}

这两种方法的区别其实就是接口和继承的区别。第一种方法更通用一些,不仅是因为更灵活,还因为它适用于后面要介绍的高级线程管理API。

Thread定义了很多方法来管理线程,包括一些静态方法,用来查看或修改线程的状态;还有一些方法用来管理线程。接下去我们简单介绍一些常用的方法。

Pausing Execution with 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]);
        }
    }
}

首先注意到sleep是个静态方法;其次需要注意到main函数声明会抛出InterruptedExcetion异常,当一个正在sleep的线程被interrupt的时候,sleep就会抛出该异常。

Interrupts

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.

A thread sends an interrupt by invoking interrupton the Thread object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption.

那线程如何support its own interruption呢?这取决于该线程当前正在做什么。如果线程经常执行像sleep这样会抛出InterruptedException的函数,当捕捉到异常的时候,一般直接从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]);
}

那如果线程很长一段时间都不会调用像sleep这样会抛出InterruptedException的函数,那我们就要自己去检查线程是否已经被中断。可以通过调用Thread.interrupted来判断,比如:

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

注意: 调用了Thread.interrupted()之后,线程的中断状态会被清除。具体看下面的解释:

The Interrupt Status Flag

The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.

By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so. However, it's always possible that interrupt status will immediately be set again, by another thread invoking interrupt.

Joins

join方法能够让一个线程等待另一个线程执行完。比如你在线程t1中调用了t2.join(),那么t1会等待t2执行完之后再执行。

join还可以接受一个参数,指明等待多长时间,和sleep一样,当收到中断信号时,该方法会抛出异常。需要注意的是,joinsleep一样,不能保证等待的时间完全准确。

举例

最后,来看一个例子,复习一下这个章节的知识点:

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

main线程创建了一个MessageLoop线程,然后每隔一秒就调用isAlive方法检查MessageLoop线程是否已经结束,如果还未结束并且已经没有了耐心,就调用interrupt方法给MessageLoop线程发中断信号;如果还未结束并且还有耐心,就调用join函数,等待MessageLoop线程一秒。

相关文章

网友评论

      本文标题:【The Java™ Tutorials】【Concurrenc

      本文链接:https://www.haomeiwen.com/subject/qcjicftx.html