zoukankan      html  css  js  c++  java
  • java中的线程池

    java中的线程池

    我们一般将任务(Task)提交到线程池中运行,对于一个线程池而言,需要关注的内容有以下几点:
    在什么样的线程中执行任务
    任务按照什么顺序来执行(FIFO,LIFO,优先级)
    最多有多少个任务能并发执行
    最多有多个任务等待执行
    如果系统过载则需要拒绝一个任务,如何通知任务被拒绝?
    在执行一个任务之前或之后需要进行哪些操作
    围绕上面的问题,我们来研究一下java中的线程池

    线程池的创建

    Exectors.newFixedThreadPool(int size):创建一个固定大小的线程池。 每来一个任务创建一个线程,当线程数量为size将会停止创建。当线程池中的线程已满,继续提交任务,如果有空闲线程那么空闲线程去执行任务,否则将任务添加到一个无界的等待队列中。
    Exectors.newCachedThreadPool():创建一个可缓存的线程池。对线程池的规模没有限制,当线程池的当前规模超过处理需求时(比如线程池中有10个线程,而需要处理的任务只有5个),那么将回收空闲线程。当需求增加时则会添加新的线程。
    Exectors.newSingleThreadExcutor():创建一个单线程的Executor,它创建单个工作者线程来执行任务,如果这个线程异常结束,它会创建另一个线程来代替。
    Exectors.newScheduledThreadPool():创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务。
    上面都是通过工厂方法来创建线程池,其实它们内部都是通过创建ThreadPoolExector对象来创建线程池的。下面是ThreadPoolExctor的构造函数。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
    						  RejectedExecutionHandler handler) {
    	...
    }
    

    我们看到构造函数是public类型的,所以我们也可以自定义自己的线程池。

    在什么样的线程中执行任务?

    java中对于任务的描述有两种,一种是Runnable型的任务,一种是Callable型的任务。前者运行结束后不会返回任何东西,而后者可以返回我们需要的计算结果,甚至异常。

    在没有返回值的线程中运行

    创建一个线程池,然后调用其execute方法,并将一个Runnable对象传递进去即可。
    ExectorService exector = Exectors.newCachedThreadPool();
    exector.execute(new Runnable(){
    public void run(){
    System.out.println("running...");
    }
    });

    在有返回值的线程中运行
    ExectorService exector = Exectors.newCachedThreadPool();
    Callable<Result> task = new Callable<Result>() {
    	public Result call() {
    		return new Computor().compute();
    	}
    };
    Future<Result> future = exector.submit(task);
    result = future.get();  //改方法会一直阻塞,直到提交的任务被运行完毕
    

    务按照什么顺序来执行(FIFO,优先级)

    如果任务按照某种顺序来执行的话,则任务一定是串行执行的。我们可以看到在ThreadPoolExecutor中第四个参数是BlockingQueue,提交的任务都先放到该队列中。如果传入不同的BlockQueue就可以实现不同的执行顺序。传入LinkedBlockingQueue则表示先来先服务,传入PriorityBlockingQueue则使用优先级来处理任务

    Exectors.newSingleThreadExcutor()使用的是先来先服务策略

    最多有多少个任务能并发执行

    线程池中的线程会不断从workQueue中取任务来执行,如果没任务可执行,则线程处于空闲状态。
    在ThreadPoolExecutor中有两个参数corePoolSize和maximumPoolSize,前者被称为基本大小,表示一个线程池初始化时,里面应该有的一定数量的线程。但是默认情况下,ThreadPoolExecutor在初始化是并不会马上创建corePoolSize个线程对象,它使用的是懒加载模式。

    • 当线程数小于corePoolSize时,提交一个任务创建一个线程(即使这时有空闲线程)来执行该任务。
    • 当线程数大于等于corePoolSize,首选将任务添加等待队列workQueue中(这里的workQueue是上面的BlockingQueue),等有空闲线程时,让空闲线程从队列中取任务。
    • 当等待队列满时,如果线程数量小于maximumPoolSize则创建新的线程,否则使用拒绝线程处理器来处理提交的任务。

    最多有多少的任务等待执行

    这个问题和BlockingQueue相关。 BlockingQueue有三个子类,一个是ArrayBlockingQueue(有界队列),一个是LinkedBlockingQueue(默认无界,但可以配置为有界),PriorityBlockingQueue(默认无界,可配置为有界)。所以,对于有多少个任务等待执行与传入的阻塞队列有关。

    newFixedThreadPoolnewSingleThreadExector使用的是LinkedBlockingQueue的无界模式。而newCachedThreadPool使用的是SynchronousQueue,这种情况下线程是不需要排队等待的,SynchronousQueue适用于线程池规模无界。

    如果系统过载则需要拒绝一个任务,如何通知任务被拒绝?

    当有界队列被填满或者某个任务被提交到一个已关闭的Executor时将会启动饱和策略,即使用RejectedExecutionHandler来处理。JDK中提供了几种不同的RejectedExecutionHandler的实现:AbortPolicy,CallerRunsPolicy, DiscardPolicy和DiscardOldestPolicy。

    AbortPolicy:默认的饱和策略。该策略将抛出未检查的RejectedExcutionException,调用者可以捕获这个异常,然后根据自己的需求来处理。

    DiscardPolicy:该策略将会抛弃提交的任务

    DiscardOldestPolicy:该策略将会抛弃下一个将被执行的任务(处于队头的任务),然后尝试重新提交该任务到等待队列

    CallerRunsPolicy:该策略既不会抛弃任务也不会抛出异常,而是在调用execute()的线程中运行任务。比如我们在主线程中调用了execute(task)方法,但是这时workQueue已经满了,并且也不会创建的新的线程了。这时候将会在主线程中直接运行execute中的task。

    在执行一个任务之前或之后需要进行哪些操作

    ThreadPoolExecutor是可扩展的,它提供了几个可以重载的方法:beforeExecute,afterExecuteterminated,这里用到了面向的切面编程的思想。无论任务是从run中正常返回,还是抛出异常而返回,afterExectue都会被调用。如果 beforeExecute中抛出了一个 RunntimeException,那么任务将不会被执行,并且 afterExecute也不会被调用。

    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicLong;
    
    public class Test {
    
    	public static void main(String[] args) {
    		TimingThreadPool executor = new TimingThreadPool(5, 10, 1,
    				TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
    		for (int i = 0; i < 5; i++)
    			executor.execute(new Runnable() {
    				@Override
    				public void run() {
    					System.out.println("running1....");
    				}
    			});
    		executor.shutdown();
    	}
    }
    
    class TimingThreadPool extends ThreadPoolExecutor {
    
    	private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    	private final AtomicLong numTasks = new AtomicLong();
    	private final AtomicLong totalTime = new AtomicLong();
    
    	public TimingThreadPool(int corePoolSize, int maximumPoolSize,
    			long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    	}
    
    	@Override
    	protected void beforeExecute(Thread t, Runnable r) {
    		super.beforeExecute(t, r);
    		startTime.set(System.nanoTime());
    	}
    
    	@Override
    	protected void afterExecute(Runnable r, Throwable t) {
    		try {
    			long endTime = System.nanoTime();
    			long taskTime = endTime - startTime.get();
    			numTasks.incrementAndGet();
    			totalTime.addAndGet(taskTime);
    		} finally {
    			super.afterExecute(r, t);
    		}
    	}
    
    	@Override
    	protected void terminated() {
    		try {
    			System.out.println(String.format("Terminated: arg time = %d",
    					totalTime.get() / numTasks.get()));
    		} finally {
    			super.terminated();
    		}
    	}
    }
    

    上面的代码统计任务平均执行时间,在每个线程中beforeExecute和afertExecute都会执行一次,而terminated等线程池关闭的时候执行

  • 相关阅读:
    苹果CMS
    rel=nofollow 是什么意思
    如何获得select被选中option的value和text和......
    使用phpexcel导出到xls文件的时候出现乱码解决
    Infinispan's GridFileSystem基于内存的网格文件系统,互联网营销 狼人:
    云计算的可伸缩性迫使App服务无状态化,互联网营销 狼人:
    那些你知道的和不知道的搜索引擎,互联网营销 狼人:
    IPv6的未来,互联网营销 狼人:
    互联网上五个最高级的搜索引擎,互联网营销 狼人:
    剖析IE浏览器子系统的性能权重,互联网营销 狼人:
  • 原文地址:https://www.cnblogs.com/xidongyu/p/6715689.html
Copyright © 2011-2022 走看看