| 
 | 
 
| 
 
 从一开始java就被设计成支持并发编程的语言,java语言及其核心类库都有对并发编程的支持。从5.0开始,Java平台引入了一些高层的并发接口。本系列文章将尽可能的概括这些内容。 
进程与线程 
    并发编程模式中,有两个基本执行单元:进程与线程。进程和线程是现代操作系统的基本概念。一个进程拥有独立完备的执行环境,进程拥有私有的计算机资源,比 如独立的内存空间、代码段、指令寄存器等等。进程在操作系统中基本等同于应用程序。最终用户看到的独立程序有可能是多个互相协作的进程,为了方便进程之间 的通信,大多数操作系统支持进程间通信(Inter Process Communication, IPC)资源,比如管道和端口。IPC往往不仅仅在同一台计算机系统上使用,也往往运用于不同计算机系统之间的通信。 
    线 程通常也称轻量级进程,线程拥有的资源比进程的要少。线程只存在于进程中,一个进程可以包含多个线程。比如一个Java程序中可以有多个线程存在。线程不 拥有独立的内存空间,而是和同进程内的其他线程共享进程的内存空间。由于线程共享进程的资源(内存或者打开的文件),同进程的线程之间往往需要大量的互斥 和同步,保证资源使用的可确定性。这在前面文章中已经说过,资源共享是并发编程中同步和互斥的根源。 
    由于进程的特性,使得它们之间资源共享的冲突比较少,因此并发编程主要是针对线程的,多线程编程是Java程序的基本特征。因此这儿讲的并发编程主要是针对Java线程编程的。 
Java线程 
    简单来说,每个Java线程都有一个Thread实例与之对应。创建线程对象的方法通常有两种: 
1.直接创建和管理,也就是每当程序需要异步执行任务时就实例化一个Thread对象,然后自己管理其生命周期。 
2.抽象线程管理,使其从程序代码中分离开来。这些接口在java 5之后的java.util.concurrency包中提供。 
    我们先讨论第一种方式,java.util.concurrency包提供的高层工具我们在后面的文章再讨论。 
    第一种方式创建线程非常直接,有两种方法可以创建一个线程实例: 
1.生成一个Runnable对象,并将它传递给Thread对象。 
public class HelloWorld implements Runnable { 
    public void run() { 
        System.out.println("Hello World"); 
    } 
    public static void main(String args[]) { 
        (new Thread(new HelloWorld())).start(); 
    } 
} 
2.继承Thread并实现其run方法: 
public class HelloThread extends Thread { 
    public void run() { 
        System.out.println("Hello World!"); 
    } 
    public static void main(String args[]) { 
        (new HelloThread()).start(); 
    } 
} 
    如何选择使用哪种方式呢?第一种模式更为通用,实现一个Runnable接口允许你继承并复用某类。第二种更简单,缺点是必须继承Thread。你可以根据具体情况选择。 
    Thread对象中定义了一些有用的方法来管理线程的生命周期: 
1.public static void sleep(long millis)throws InterruptedException方法,该方法挂起当前线程一段时间,主动让出处理器让其他线程使用。sleep方法还可以用来控制执行的速度。注意这个方法在挂起线程时,有可能被其他线程中断挂起,因此不能依赖其时间参数来定时。 
2.public void interrupt()方法,该方法中断某线程正在做的事情,告诉它某些事情发生了需要处理。程序员需要捕捉这个中断异常,并在异常处理中执行应该做的动 作。接受中断的方式有两种,一种是被中断线程目前正在执行一个能抛出InterruptedException的方法,比如sleep或者 Object.wait等方法,还比如一些可以被interrupted的SeverSocket.accept方法等等。下面是示例代码: 
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); 
} 
    另一种是程序正在执行一些不抛出InterruptedException的动作,这时该线程要负责定期使用interrupted()方法检查当前线程是否受到中断信号,然后做相应处理。下面是示例代码: 
for (int i = 0; i < inputs.length; i++) { 
    heavyCrunch(inputs); 
    if (Thread.interrupted()()) { 
        //We've been interrupted: no more crunching. 
        return; 
    } 
} 
    SwingWorker的cancel方法就是采用的第二种模式实现的,SwingWorker的cancel方法调用其任务线程的 interrupt()方法,而doInBackground方法应该定期调用SwingWorker的isCanceled方法判断当前任务是否被取 消。isCanceled()最终调用了Thread.interrupted()方法检测当前线程是否接受到中断信号。 
3.public static Thread currentThread()获得当前线程对象。 
4.public boolean isInterrupted()该线程是否受到中断信号。 
5.public static boolean interrupted(),检测当前线程是否接受到中断信号。它实际上是调用了Thread.currentThread().isInterrupted()实现的。 
6.public final void join()该方法允许一个线程等待另一个线程的结束。比如t是一个目前正在执行的线程,那么t.join()将目前线程挂起直至线程t结束。 
线程示例 
    下面举例总结本节所说明的一些概念。SimpleThreads由两个线程组成,第一个是主线程,主线程使用一个Runnable对象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); 
                } 
            } 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!"); 
    } 
} |   
 
 
 
 |