方法四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");
}
}