ThreadPoolExecutor原理与使用

ThreadPoolExecutor是java.util.concurrent包提供的基础线程池,使用非常广泛

让我们来看一下线程池的使用和内部实现原理

下面是ThreadPoolExecutor的一个构造方法,最终所有其他构造方法都要调用这个构造方法,来看一下构造方法中的参数的作用

corePoolSize:核心线程池的大小,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。调用perstartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程

maximumPoolSize:线程池允许创建的最大线程数。如果队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。如果是无界队列这个参数不起作用

keepAliveTime:当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。这里把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。

unit:时间单元

workQueue:线程池的工作队列,工作队列最常用的有如下几种

    ArrayBlockingQueue:是一个基于数组结构的游街阻塞队列

    LinkedBlockingQueue:基于链表结构的阻塞队列

    SynchronousQueue:不存储元素的阻塞队列,插入元素后必须等待另一个线程调用移除操作

threadFactory:创建线程的工厂

handler:饱和策略。当队列和线程都满了,必须采取一种策略处理提交的新任务。默认策略是AbortPolicy,JDK1.5提供了如下4种策略,除了这四种策略外还可以自己来实现RejectedExecutionHandler接口来实现自定义的策略

    AbortPolicy:直接抛出异常

    CallerRunsPolicy:只用调用者所在线程来运行任务

    DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

    DiscardPolicy:不处理,丢弃掉

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
线程池的实现原理

当向线程池提交一个任务之后,线程池的处理流程如下:

1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务(使用threadFactory来创建)。如果核心线程池里的线程都在执行任务,则进入下个流程

2)线程池判断工作队列是否已经满了。如果工作队列没有满,则将新提交的任务存储在这个工作队列里(对应构造函数中的workQueue),则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程

3)线程池判断线程池是否已经满了(线程达到了最大数,且任务队列都满了)。如果没有,创建一个新的工作线程来执行任务。如果已经满了,任务将被拒绝并交给饱和策略来处理这个任务

线程池创建好了之后就可以向线程池里提交任务了

向线程池提交任务有两种方法,分别是execute()和submit()方法

区别是execute()方法用于提交没有返回值的任务,submit()方法用于提交需要返回值的任务。线程池会返回一个Future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,还可以使用重载的get()方法阻塞一段时间后返回不管任务是否执行完

public class ThreadPoolExecutorDemo {
	public static void main(String[] args) {
		ThreadPoolExecutor tpe =
				new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque());
		tpe.execute(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("执行run方法");
				
			}
		});
		
		Future future =tpe.submit(new Callable() {

			@Override
			public String call() throws Exception {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("执行call方法");
				return "success";
			}
		});
		for(int i=0;i<5;i++){
				String str=null;
				try {
					str = future.get(2, TimeUnit.SECONDS);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (TimeoutException e) {
					System.err.println("超时未返回");
				}
				System.out.println("get方法返回值为:"+str);
		}
	}
}

ps:如果需要了解并发相关的知识建议以下两本书《JAVA并发编程实践》,《JAVA并发编程的艺术》。

你可能感兴趣的:(Java)