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

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;
	}
	@Override
	public 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);
		//执行可以有返回值的线程1
		Future<Double> future = executorService.submit(new Callable<Double>(){
			@Override
			public 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>(){

			@Override
			public 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>() {
			@Override
			public 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>() {

			@Override
			public 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
}


你可能感兴趣的:(Java基础--并发实用工具(3))