实现并发最直接的方式是在操作系统级别使用进程,进程是运行在它自己的地址空间内的自包容的程序。多任务操作系统可以通过周期性地将CPU从一个进程切换到另一个进程,来实现同时运行多个进程。

并发提供了一个重要的组织结构上的好处,可以使程序设计极大地简化。

下面的LiftOff任务将显示发射之前的倒计时:

public class LiftOff implements Runnable{
    protected int countDown=10;
    private static int taskCount=0;
    private final int id=taskCount++;
    public LiftOff(){}
    public LiftOff(int countDown){
        this.countDown=countDown;
    }
    public String status(){
        return "#"+id+"("+(countDown>0?countDown:"LiftOff")+").";
    }
    public void run(){
        while(countDown-->0){
            System.out.println(status());
            Thread.yield();
        }
    }
    public static void main(String[] args) {
        LiftOff lo=new LiftOff();
        lo.run();
    }
}
不同任务的执行在线程被换进换出时混在了一起,这种交换是由线程调度器自动控制的。

如:

public class MoreBasicThreads {
    public static void main(String[] args) {
        for(int i=0;i<5;i++)
            new Thread(new LiftOff()).start();;
            System.out.println("Waiting for LiftOff");
    }
}

Executor用来管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层,与客户端直接执行任务不同,这个中介对象将执行任务。Executor可以用来管理异步任务的执行,而无须显式地管理线程的生命周期。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService exec=Executors.newCachedThreadPool();
        for(int i=0;i<5;i++)
            exec.execute(new LiftOff());
        exec.shutdown();
    }
}

Runnable是执行工作的独立任务,但是它不返回任何值。如果希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口.

import java.util.concurrent.Callable;

public class TaskWithResult implements Callable<String>{
    private int id;
    public TaskWithResult(int id){
        this.id=id;
    }
    public String call(){
        return "result of TaskWithResult"+id;
    }
}

 

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableDemo {
    public static void main(String[] args) {
        ExecutorService exec=Executors.newCachedThreadPool();
        ArrayList<Future<String>> results=new ArrayList<Future<String>>();
        for(int i=0;i<5;i++)
            results.add(exec.submit(new TaskWithResult(i)));
        for(Future<String>fs:results)
            try{
                System.out.println(fs.get());
            }catch(InterruptedException e){
                System.out.println(e);
                return;
            }catch(ExecutionException e){
                System.out.println(e);
            }finally{
                exec.shutdown();
            }
    }
}

影响任务行为的一种简单方法是调用sleep(),这将使任务中止给定的时间。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SleepingTask extends LiftOff{
    public void run(){
        try{
            while(countDown-- > 0){
                System.out.println(status());
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch(InterruptedException e){
            System.err.println("Interrupted");
        }
    }
    public static void main(String[] args) {
        ExecutorService exec=Executors.newCachedThreadPool();
        for(int i=0;i<5;i++)
            exec.execute(new SleepingTask());
        exec.shutdown();
    }
}
附加:对sleep()的调用可以抛出InterruptedException异常,上述例子中,其早run()中被捕获,因为异常不能跨线程传播回main(),所以必须在本地处理在任务内部产生的异常。

上述任务是完美分布的,0到4,再回头0到4。这是很有意义的,在每个打印语句之后,每个任务都将要睡眠,这使得线程调度器可以切换到另一个线程,进而驱动另一个任务。

注意!:顺序行为依赖于底层的线程机制,这种机制在不同的操作系统之间是有差异的,因此,不能依赖于sleep()!  而必须控制任务执行的顺序。

 

 

 

---恢复内容结束---

posted on 2015-09-24 23:06  baraka  阅读(142)  评论(0编辑  收藏  举报