方法四Executors框架


import java.util.concurrent.*;

/**
 * 1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。
 * 要执行任务的人只需把Task描述清楚,然后提交即可。
 * 这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。
 * 具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。
 * Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。
 * 因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,
 * 还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。
 *
 * 关于ExecutorService执行submit和execute的区别
 * 1、接收的参数不一样
 * 2、submit有返回值,而execute没有
 * 3、submit方便Exception处理
 *    如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。
 */
public class M04ExccutorCallable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService1 = Executors.newSingleThreadExecutor();
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();


        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                for(int i = 0; i < 3; i++) {
                    System.out.println("Runnable接口"+i);
                }
            }
        };
        //执行Runnable接口,无返回值
        executorService1.execute(runnable);
        executorService1.shutdown();


        //执行Callable接口,有返回值
        Callable<String> stringCallable = new Callable(){
            @Override
            public String call() throws Exception {
                for(int i = 0; i < 3; i++) {
                    System.out.println("Callable接口"+i);
                }
                return "Hello World";
            }
        };
        Future<String> submit2 = executorService2.submit(stringCallable);
        executorService2.shutdown();
        //get操作会造成执行线程的阻塞
        String submit2Res = submit2.get();
        System.out.println(submit2Res);

        System.out.println("main Thread output");
    }
}