这篇文章主要讲解了“Java中的线程池有什么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的线程池有什么用”吧!
Java中的线程池
【1】使用线程池的好处:
1)降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2)提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行。 3)提高线程的可管理性。线程池可以进行统一分配、调优和监控线程。
【2】构造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler); 参数说明: 1)corePoolSize:核心池的大小。 1)在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务。 2)默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务。 注意:即使其它空闲的线程能够执行新任务也会去创建线程,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。 3)调用prestartAllCoreThreads()或prestartCoreThread()方法来预创建线程,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。 2)workQueue:任务缓存队列,是一个阻塞队列,用来存储等待执行的任务;类型为BlockingQueue<Runnable>,通常可以取下面4种类型: 1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小; 2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQueue。 eg:静态工厂方法Executors.newFixedThreadPool()就是使用的这个队列。 3)SynchronousQueue:这个队列不会保存提交的任务,而是直接新建一个线程来执行新来的任务。 说明:每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。 eg:静态工厂方法Executors.newCachedThreadPool使用的就是这个队列。 4)PriorityBlockingQueue:一个具有优先级的无限阻塞队列。 说明: 1>一般使用LinkedBlockingQueue和SynchronousQueue 2>建议使用有界队列,有界队列能增加系统的稳定性。 eg:如果线程池里的工作线程全部阻塞,任务积压在线程池里,如果设置成无界队列,那么这个队列会越来越大,有可能会撑满内存,导致整个系统不可用。 3)maximumPoolSize:线程池中允许创建的最大线程数。 1)如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程来执行任务。 2)如果使用了无界的任务队列,则这个参数就不起什么作用了(队列默认的大小是:Integer.MAX_VALUE,在队列未满之前,线程池是不会再去创建新线程了)。 4)RejectedExecutionHandler:任务拒绝策略,当任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略: 1)ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常,默认使用该策略。 2)ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常;会导致被丢弃的任务无法再次被执行 3)ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程);会导致被丢弃的任务无法再次被执行 4)ThreadPoolExecutor.CallerRunsPolicy: 由调用线程处理该任务;主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 说明:也可以实现RejectedExecutionHandler接口来自定义策略。 5)keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用。 但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0; 6)TimeUnit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性: TimeUnit.DAYS; 天 TimeUnit.HOURS; 小时 TimeUnit.MINUTES; 分钟 TimeUnit.SECONDS; 秒 TimeUnit.MILLISECONDS; 毫秒 TimeUnit.MICROSECONDS; 微妙 TimeUnit.NANOSECONDS; 纳秒 7)ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
【3】其它方法:
任务的提交: execute(Runnable command): 提交后没有返回值,故无法判断任务是否被线程池执行成功。 submit(): 提交后返回一个Future对象,通过这个future对象可以判断任务是否执行成功。 通过future的get()方法来获取返回值,该方法会阻塞当前线程直到任务完成。 get(long timeout, TimeUnit unit)方法:阻塞当前线程一段时间后立即返回,这时候任务可能没有执行完。 线程池容量的动态调整: setCorePoolSize() 设置核心池的大小 setMaximumPoolSize() 设置线程池最大能创建的线程数 线程池的关闭: 原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。 shutdown() 1>将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。 2>不再接受新的任务,但是不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止。 shutdownNow() 1>首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并且清空任务缓存队列,并返回等待执行任务的列表. 说明: 1)只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。 2)当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。
【4】线程池的状态:
// RUNNING状态:线程池正常运行,可以接受新的任务并处理队列中的任务 private static final int RUNNING = -1 << COUNT_BITS; // SHUTDOWN状态:不再接受新任务,但是会执行队列中的任务 private static final int SHUTDOWN = 0 << COUNT_BITS; // STOP状态:不再接受新任务,不处理队列中的任务,中断正在处理的任务 private static final int STOP = 1 << COUNT_BITS; // 过渡状态:所有的任务都执行完了,线程池已经没有有效的线程了,此时线程池的状态为过渡状态,并且将要调用terminated()方法 private static final int TIDYING = 2 << COUNT_BITS; // 终止状态:terminated()方法调用完成后的状态 private static final int TERMINATED = 3 << COUNT_BITS; 1>当线程池刚创建后,线程池处于RUNNING状态,可以接受新的任务并处理队列中的任务 2>如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕; 3>如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务; 4>当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
【5】线程池的处理流程:
ThreadPoolExecutor执行execute方法分下面4种情况。 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。 2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。 3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。 4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution()方法。
感谢各位的阅读,以上就是“Java中的线程池有什么用”的内容了,经过本文的学习后,相信大家对Java中的线程池有什么用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。