Executer讲解

发表时间:2017-09-07 16:39:52 浏览量( 16 ) 留言数( 0 )

学习目标:

1、了解ThreadFactory。

2、了解Executor和ExecutorService


学习过程:

一、ThreadFactory

   使用线程工厂就无需再手工编写对 new Thread 的调用了,根据需要创建新线程的对象,一般我们可以自定以ThreadFactory,代码如下:

public class MyThreadFactory implements ThreadFactory {

	private static final AtomicInteger threadNumber = new AtomicInteger(1);

	private final String name;

	private final boolean daemo;

	private final ThreadGroup threadGroup;

	public MyThreadFactory() {
		this("NettyThreadFactory-thread-" + threadNumber.getAndIncrement(), false);
	}

	public MyThreadFactory(String name) {
		this(name, false);
	}

	public MyThreadFactory(String nameTemp, boolean daemoTemp) {
		name=nameTemp;
		daemo = daemoTemp;
		SecurityManager s = System.getSecurityManager();
		threadGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
	}

	public Thread newThread(Runnable r) {
		Thread ret = new Thread(threadGroup, r, name, 0);
		ret.setDaemon(daemo);
		return ret;
	}

}


二、Executor和ExecutorService

   前面说的ThreadFactory可以说隐藏了建立线程的步骤,那么和Executor结合使用会得到很好的效果,Executor可以隐藏了线程的直接执行。Executor框架使用接口从任务执行中解藕任务的提交操作,Executor声明了一个单独的void execute (Runnable runnable)方法,该方法会在某个时间点执行这个名为Runnable 的任务。Executor只关注Runnable 接口。由于Runnable的run()方法没有返回值,这对于一个可运行的任务而言,就没法简单地把值返回给调用者。除此之外Executor还有以下的一些缺点。

• Executor 没有提供一种方式来追踪可运行任务的运行过程,例如, 正在执行、正在取消了一个执行中的任务或者确认什么时候任务完成执行。

• Executor 无法执行一组可运行任务。

• Executor 没有为应用程序提供一种方式来关闭一个executor 。

    这些限制被java.util.concurrent.ExecutorService 接口解决了,这个接口扩展Executor接口并且其实现就是一个典型的线程池,所以在我们以后开发中经常使用的是ExecutorService

   Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类:

1. ThreadPoolExecutor

2. ScheduledThreadPoolExecutor

    创建一个什么样的ExecutorService的实例(即线程池)需要根据具体应用场景而定,不过Java给我们提供了一个Executors工厂类(注意区分Executor接口和Executors工厂类),它可以帮助我们很方便的创建各种类型ExecutorService线程池,Executors一共可以创建下面这四类线程池:

1. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

   有关这四个线程池的使用我们在下一节可再说,我们先看看ExecutorService和上面的ThreadFactory的结合使用:

	public static void main(String[] args) {  
	        ExecutorService exec=Executors.newFixedThreadPool(3,new MyThreadFactory());  
	        
	        for(int i=0;i<3;i++) {  
	        	//submit()时会调用DaemonThreadFactory类的newThread()方法来创建线程。
	            exec.submit(new Runnable() {
					public void run() {
						System.out.println("hello");
					}
				});  
	        }  
	        exec.shutdown();  
	}


三、ExecutorService的基本使用

当然我们也可以不适用ThreadFactory直接使用线程,ExecutorService有如下几个执行方法:

- execute(Runnable)

- submit(Runnable)

- submit(Callable)

- invokeAny(...)

- invokeAll(...)


1、 execute(Runnable)

    这个方法接收一个Runnable实例,并且异步的执行,请看下面的实例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {

public void run() {

    System.out.println("Asynchronous task");

}

});

executorService.shutdown();

这个方法有个问题,就是没有办法获知task的执行结果。如果我们想获得task的执行结果,我们可以传入一个Callable的实例(下面会介绍)。


2、submit(Runnable)

    submit(Runnable)和execute(Runnable)区别是前者可以返回一个Future对象,通过返回的Future对象,我们可以检查提交的任务是否执行完毕,请看下面执行的例子:

Future future = executorService.submit(new Runnable() {

public void run() {

    System.out.println("Asynchronous task");

}

});


future.get();  

如果任务执行完成,future.get()方法会返回一个null,但是future.get()方法会产生阻塞,直到线程结束。


3、submit(Callable)

    submit(Callable)和submit(Runnable)类似,也会返回一个Future对象,但是除此之外,submit(Callable)接收的是一个Callable的实现,Callable接口中的call()方法有一个返回值,可以返回任务的执行结果,而Runnable接口中的run()方法是void的,没有返回值。请看下面实例:

Future future = executorService.submit(new Callable(){

public Object call() throws Exception {

    System.out.println("Asynchronous Callable");

    return "Callable Result";

}

});

System.out.println("future.get() = " + future.get());

如果任务执行完成,future.get()方法会返回Callable任务的执行结果。future.get()方法会产生阻塞,直到线程结束并打印线程的返回结果。