java线程池

来源:互联网 发布:战龙三国旗子进阶数据 编辑:程序博客网 时间:2024/06/06 00:48
    网上很多大神都写过这个话题了,而且写得很详尽,自己根据自己学习中觉得难的和容易以往的要点写一篇与之相关的。   首先,我觉得应该先理清java线程池中几个重要的类Executors,ExecutorService,AbstractExecutorService,ThreadPoolExecutor,ScheduledExecutorService,ScheduledThreadPoolExecutor,这几个类他们的关系如文章最后图:   可以看出Executor是顶层接口,它只有一个函数便是execute(),下面的ExecutorService基本上具有java线程池具有的全部功能(除了ScheduledTreadPoolExecutor,这从图中应该也能略知一二)有以下几种函数: 1.execute(Runnable r) 2.submit(Runnable r)     3. submit(Callable c)     4. shutDown()     5. shutDownNow()     6. invokeAny(Cllection<? extends Callable> tasks)     7. invokeAll(Collection<? extends Callable> tasks)

这些函数是ThreadPoolExecutor的实例都具有的函数,1,的作用就是利用现有的线程池进行任务的执行,2和1的作用差不多其返回一个Future实例但是由于其类型没确定一般返回空,但是除了返回值外他的功能和3一样,就是通过返回的future对象能够对提交的任务进行管理如取消任务,查看是否被取消,判断是否正常结束等。至于第4点看名字就知道了是中断任务获取,这个意思是当前正在运行的任务和在等待队列的任务会被执行完,但是不再接受新任务;第5点是尝试性强制中断现有任务包括正在执行的,但是他不能保证肯定会成功(现有的java机制中没有立刻中断线程这一机制了,只有被中断的线程响应相应的中断操作才会被中断,以后有时间我们也讲讲java中的中断机制)。至于第6,7点也是线程池的执行任务方式之一,前者则是在提交的任务中任意选择一个进行执行,但不会返回future对象而是如果在无异常情况下则会直接返回结果,而后者会返回一个future列表,并且其顺序和给定的任务顺序一致。

讲了线程池的使用,我们再来讲讲线程池的创建,java已经做好的几种线程池有两种创建方法,一个是通过new ThreadPoolExecutor(int corepoolsize,int maxpoolsize,long keepalivetime,TimeUnit u,BlockingQueue queue,ThreadFactory threadFactory,
RejectedExecutionHandler handler);这种方式的创建方式可能给了我们更大的选择权,让我们根据自己的需要来完全决定我们所要采取的线程池结构,而另一种,则是java的线程池构造器Executors来产生java事先定义好的几种线程池其中有四种:
1. Executors.newSingleThreadPool()
2. Executors.newFixedThreadPool()
3. Executors.newCachedThreadPool()
4. Executors.newScheduledThreadPool()
来,我们看下第1个的构造函数,new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));可以看到,其将corepoolsize和maxpoolsize都设置成1而keepalivetime=0则表明不设置超时时间,等待队列设置的是用LinkedBlockingQueue无界队列,这里面缺少我们上面说的ThreadFactory和RejectedExceptionHandler两个参数那么用的就是默认值其中ThreadFactory用的是DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = “pool-” +
poolNumber.getAndIncrement() +
“-thread-“;
}而我们所说的满时拒绝策略则是使用“来则拒绝,并抛出异常”策略;第2中调用的是new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());第3个调用的是new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());第4个调用的是:new ScheduledThreadPoolExecutor(corePoolSize);
值得讲一下的是他们在构造线程池所使用的等待阻塞队列有点差异前两种都是用的是LinkedBlockingQueue第3个用的是SynchronousQueue原因我想很简单应为cachedThreadPool的核心池是无限大的,只要有任务来便进入核心线程进行运行,所以选择具有“转运”特性的SynchronousQueue最为合适,而前两者因为核心池具有大小限制,所以选择“生产-消费”特性的LinkedBlockingQueue比较合适(生产特性体现得不是太明显哈);第4种则是使用了DelayWorkQueue队列,详细细节大家自己可看源码。

这里我想就着它们的构造方法说一下当线程进入线程池的一个过程:

  1. 如果此时线程池的线程数小于corepoolsize,那么首先线程池会创建线程来运行所提交的任务。
  2. 如果此时线程池的线程大于corepoolsize且等待队列,那么提交的任务会进入等待队列
  3. 如果等待队列满了,且线程数小于maxpoolseze,那么提交的任务会新建线程来运行
  4. 如果提交的任务数大于maxpoolsize那么,就会采用事先决定的RejectedExceptionHandler来处理。
    这下大家该知道这个corepoolsize和maxpoolsize的区别与联系了吧。

对于线程池的好处,一是感觉为了节约资源,不用每来一个任务就创建一个线程去运行,运行完还要销毁,要知道这些操作是很耗时的,第二感觉是快:能够在那些已创建好的线程利用原来的空闲线程来运行现有任务,第三方便管理,或者说管理的思路比较清晰。

至于使用上,对于SingleThreadPool,他是一个容量只有1的线程池,并且它的最大线程池和核心池容量一样(被设为无效不起作用了)适合一些让任务顺序单个执行的场景。每执行完一个任务就再从等待队列重新取出一个;CachedThreadPool则是一个核心池无限大的线程池,但是他不会无限的创建线程,而是每次在等待队列取任务时都看看是否有以前使用过的可以reuse得线程,如果有则不必新建,没有则新建,使用的时候,应该特别注意资源问题;FixedThreadPool则非常简单,就是创建一个固定数目核心池大小的线程池,在对我们的应用程序的访问资源需求进行很好的分析后,我们可以使用这个线程池来装配我们要运行的任务,他的maxpoolsize也被设为无效,也就是只有核心池与等待队列之说了;至于ScheduledThreadPool则是一个需要将任务周期或延时运行的线程池。他有schedule(Runnable r),scheduleAtFixedRate(),scheduleWithFixedDelay()等函数来执行任务且会返回future对象,可管理要周期或延时的任务。至于功能上的区别,scheduleAtFixedRate(Runnable r,int initialdelay,int delay,TimeUnit u)和scheduleWithFixedDelay(Runnable r,int initialdelay,int delay,TimeUnit u)功能相似,都是将线程池中的任务以固定的周期或延迟周期运行,他俩区别是scheduleAtFixedRate()如果任务本身运行的时间比延时(delay)的时间还长那么任务运行时候就会忽视delay,上一个任务运行完则再次运行;而scheduleWithFixedDelay()则不管单个任务运行时间,每次上一个任务运行完那么下一个任务就会延时delay然后再再次运行,这里面的线程池的核心池大小和其他三个的概念一样,就是能放多少个待周期运行的任务数目;而schedule()函数则只是实现将线程池的任务延时运行一次,不像上面两个会周期运行。
这里写图片描述

原创粉丝点击