Java基础--并发实用工具(3)

来源:互联网 发布:篮球身体素质知乎 编辑:程序博客网 时间:2024/05/16 08:19

1.执行器

并发API提供了一种称为执行器(线程池)的特性,用于启动和控制线程的执行,因此,执行器为线程的管理提供了一种替代方案。
执行器的核心是Executor接口。主要方法有:void execute(Runnable thread):用于启动一个线程;ExecutorService接口扩展了Executor接口,添加了用于帮助管理和控制线程执行的方法:void shutdown():用于关闭执行器,如果执行器不被关闭,则会一直运行,程序不会终止,除非显式关闭了执行器。ScheduledExecutorService接口,该接口扩展了ExecutorService接口,用于支持线程调度。
并发API提供的执行器实例类总共3个:ThreadPoolExecutor(实现了Executor接口和ExecutorService接口)、ScheduledThreadPoolExecutor(实现了Executor和ScheduledExecutorService接口)、ForkJoinPool。
除了ForkJoinPool在后续博文介绍Fork/Join框架的时候探讨,这里对前两个执行器类做实例代码展示。
ThreadPoolExecutor实例代码展示:
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExecutorServiceTest1 {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);executorService.execute(()->{System.out.println("thread-0 running");});executorService.execute(()->{System.out.println("thread-1 running");});executorService.execute(()->{System.out.println("thread-2 running");});executorService.execute(()->{System.out.println("thread-3 running");});executorService.execute(()->{System.out.println("thread-4 running");});//如果不调用Executor的shutdown方法,程序并不会终止,因为执行器仍在运行executorService.shutdown();}//运行结果://thread-0 running//thread-1 running//thread-2 running//thread-3 running//thread-4 running}
ScheduledThreadPoolExecutor实例代码如下:
import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ScheduledExecutorServiceTest {public static void main(String[] args) {/* * 用于定时执行任务 */ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);//初始延迟是1秒,然后每隔两秒执行一次ses.scheduleWithFixedDelay(new ThreadForExecutorServiceTest2(ses), 1, 2, TimeUnit.SECONDS);}}class ThreadForExecutorServiceTest2 implements Runnable{int i = 5;ScheduledExecutorService ses;public ThreadForExecutorServiceTest2(ScheduledExecutorService ses) {this.ses = ses;}@Overridepublic void run() {while(i>0){i--;System.out.println("running...  "+i);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}//如果不关闭执行器,程序不会停止ses.shutdown();}}

2.Callable接口和Future接口

其实,看到这里,就能发现一个问题:至此使用实现Runnable接口(Thread类也是实现了Runnable接口)的方式创建的线程是没有返回值的,也就是线程执行完了就是执行完了,如果在线程执行完了想返回点东西给调用线程,这就用到了Callable接口和Future接口。Callable接口用于创建有返回值的线程,Future接口用于接收返回值。具体的API分析如下:
Callable接口是泛型接口:interface Callable<V>:V指明任务返回的数据类型,这个接口只有一个方法:V call()在call方法中定义希望执行的任务,在任务执行完成后返回结果,如果不能计算结果,call方法必须抛出异常。
Callable任务通过ExecutorService接口的submit方法执行。submit方法有三种方式,其中一种用于执行Callable任务:<T> Future<T> submit(Callable<T> task):task是要执行的任务,Future用于接收返回值。
Future接口是泛型接口:interface Future<V>:V指明要接收的结果数据类型。为了获取返回值,使用下面两种方法获取:V get()、V get(long wait,TimeUnit tu),一种是无限制的等待,一种是添加了等待超时的等待。
使用这两个接口创建有返回值线程的实例代码如下:
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class CallableFutureTest {public static void main(String[] args){//获取线程池ExecutorService executorService = Executors.newFixedThreadPool(2);//执行可以有返回值的线程1Future<Double> future = executorService.submit(new Callable<Double>(){@Overridepublic Double call() throws Exception {double sum=0;for(int i = 1;i<=10;i++){sum+=i;}return sum;}});Future<String> future2 = executorService.submit(new Callable<String>(){@Overridepublic String call() throws Exception {//在这个线程中获取上一个线程的返回值,并使用,如果执行的时候还没有出结果,那就等,直到出结果if(future.get()==55.0){System.out.println("Got the return valre of last thread...");}return "the second thread returned..";}});try {System.out.println(future2.get());} catch (InterruptedException | ExecutionException e) {System.out.println("Exception happend..");e.printStackTrace();}executorService.shutdown();}/* * 因为由实现Runnable接口来创建的线程,线程任务都是由run方法中的内容决定的,而run方法没有返回值 * 需求来咯:如果我想在线程运行完了之后返回一个东西给调用线程呢?这就要用到Callable接口和Future接口了 *///运行结果://Got the return valre of last thread...//the second thread returned..}

3.时间单位TimeUnit

并发API定义了一些方法,这些方法的参数中使用了TimeUnit类型,用于指明超时时间。TimeUnit是用于指定计时单位(或时间粒度)的枚举。尽管在调用使用TimeUnit的方法时,TimeUnit可以指定任何粒度的值,但是并不能保证系统是否能够达到指定的粒度。
使用TimeUnit的示例代码如下:
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;import java.util.concurrent.TimeoutException;public class TimeUnitTest {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);Future<Double> future = executorService.submit(new Callable<Double>() {@Overridepublic Double call(){int sum = 0;for(int i = 0;i<=10;i++){sum+=i;}try {//故意让着线程睡一会,然后另外一个线程就在特定时间内拿不到结果,超时后不再等待Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return (double) sum;}});Future<String> future2 = executorService.submit(new Callable<String>() {@Overridepublic String call(){double d = 0.0;try {d = future.get(10,TimeUnit.MILLISECONDS);} catch (InterruptedException | ExecutionException | TimeoutException e) {if(e instanceof TimeoutException)//确定是发生了超时异常System.out.println("Exception happend...");}if(d==55.0){return "Got the return value of the first thread";}return "Time out:Got null from the first thread";}});try {System.out.println(future2.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executorService.shutdown();}/* * 是时间单位类,譬如线程等待的时候,可以指明等待的时间,或者等线程的返回结果,也有等待时间 * 超过了这个时间,就不再等待了,超时没有拿到东西或者超时等待了,就要抛出超时异常,毕竟指定时间内没给我答案 *///运行结果://Exception happend...//Time out:Got null from the first thread}


1 0
原创粉丝点击