Java多线程——Executor框架

一、显示地创建线程

Java中显示地创建一个线程,只需要创建Thread(或者其子类,或者实现了Runnable的类)的实例即可。通过调用start()方法就可以启动该线程。

例子:

Thread thread = new Thread(task){
	@Override
	public void run() {
		// Blablabal
	}
};

thread.start();

当在主函数中执行上述代码片段的时候,该任务将被从主线程中分离出来,在它自己的线程中去执行。但是如果我们简简单单的为每一个任务创建一个线程,那么在任务过多的时候将会出现问题:

1. 消耗资源:活跃的线程将消耗系统资源,尤其是内存;

2. 额外的开销:线程的创建,切换与销毁都是需要开销的;

3. 稳定性:线程数是有个上限的,超过它可能会导致OutOfMemoryError,而且这个上限又是与平台相关的。


二、Executor接口

Executor本身是一个接口,它的定义非常简单,如下:

public interface Executor {
	void executor(Runnable command);
}
然而Executor是非常灵活的,因为它把任务的提交与执行解耦开来,从而使系统的线程数可控,而并非由任务数决定。例子:

public class DemoExecutor implements Executor {
	@Override
	public void execute(Runnable command) {
		new Thread(command).start();
	}
}

Executor e = new DemoExecutor();
Runnable task = new Runnable() {
	@Override
	public void run() {
		// Blablabla
	}
};
e.execute(task);
Executor实际上是一种生产者—消费者模式,提交任务相当于生产者,而执行任务是消费者。所以说Executor很适合在生产消费模式的系统里面使用。要注意到的是,把任务的提交和执行分离开来,使我们能够更加轻松地实现一些机制和策略,例如说我们可以在executor中定义是否执行某个线程等等。


三、管理Executor生命周期:ExecutorService

ExecutorService扩展了Executor接口,为其提供了管理生命周期的方法(也有一些便于任务提交的方法):

public interface ExecutorService extends Executor {
    void shutdown();  
    List<Runnable> shutdownNow();  
    boolean isShutdown();  
    boolean isTerminated();  
    boolean awaitTermination(long timeout, TimeUnit unit)  
  
    <T> Future<T> submit(Callable<T> task);  
    <T> Future<T> submit(Runnable task, T result);  
    Future<?> submit(Runnable task);  
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)  
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)  
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,  
                    long timeout, TimeUnit unit)  
}  

三种状态:关闭,终止,运行。

shutdown是一种平缓的关闭Executor的方式——不接受新的任务,完成已经提交的任务

shutdownNow是比较暴力的,它会试图强制关闭所有执行中的任务,同时当然也不会再接受新任务

关闭后的接受的任务将会由Reject Execution Handler来处理。

awaitTermination阻塞直到所有任务完成,响应中断,可以设置timeout。当awaitTermination之后,通常会shutdown,以关闭Executor。

ExecutorService的例子:

public class DemoExecutorService {
	private final ExecutorService exec = Executors.newSingleThreadExecutor();
	
	private void handler() {
		// Blablabla
	}
	
	public void run() {
		while (!exec.isShutdown()) {
			try {
				exec.execute(new Runnable() {
					@Override
					public void run() {
						handler();
					}
				});
			}catch (RejectedExecutionException e) {
				// Handling exception
			}
		}
	}
}



四、一些线程池

线程池维护了一定数量的可重用的线程。我们可以想象它的好处:1. 线程池的规模是可控的,防止线程数量过多的情况;2. 线程是可复用的,减少了创建销和毁线程的开销。

Executors类的静态工厂方法newXXThreadPool提供了创建和配置以下几种线程池的方法:

1. newFixedThreadPool

固定大小的线程池,每当提交一个任务时就创建一个,直到线程数量达到最大值。如果有异常结束的线程,会有新的线程被补充进来。

2. newCachedThreadPool

有弹性的线程池,没有线程数量的限制,会根据具体环境回收和创建线程。

3. newSingleThreadExecutor

单个线程的线程池,如果异常结束会有新的线程替代。

4. newScheduledThreadPool

固定大小的线程池。Scheduled表明这是一个定时的(或者延迟的)执行任务的线程池。


五、Future & Callable

由于有些运算是很耗时间的,而有的时候我们并不一定要第一时间拿到结果,那么就可以通过Future和Callable这两个接口来实现。

public interface Callable<V> {
	V call() throws Exception;
}

public interface Future<V> {
	boolean cancel(boolean mayInterruptIfRunning);
	boolean isCancelled();
	boolean isDone();
	V get() throws InterruptedException, ExecutionException,CancellationException;
	V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException;
}
Callable和Runnable很类似,Callable的优点在于 1. 可以有返回值;2. 可以抛出异常。

Future表明任务的生命周期,其核心在于get方法。get会阻塞直到任务完成,或者抛出异常。Future的获取方式很多,其中之一是通过ExecutorService的submit方法提交一个Callable。

例子:

ExecutorService exec = Executors.newSingleThreadExecutor();
Future<void> future = exec.submit(new Callable<void> { public void call() { // Blablabla } });
try {
	future.get();
} catch (InterruptedException e) {
	// Blablabla
} catch (CancellationException e) {
	//Blablabla
} catch (ExecutionException e) {
	//Blablabla
}

另外提一下FutureTask,它实现了Future和Runnable两个接口,因此既可以作为Future又可以被Executor执行。


六、总结

Executor框架把提交与执行解耦,使我们可以更加容易地实现灵活的执行机制和线程管理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值