本文主要介绍Java线程池的功能和使用方法,帮助您更好地理解和学习Java的相关知识。感兴趣的朋友可以了解一下。
服务器端应用程序(如数据库和Web服务器)需要处理来自客户端的高并发和耗时短的请求,因此频繁创建处理这些请求所需的线程是一项非常消耗资源的操作。传统的方法是为新请求创建一个新线程。尽管这种方法看起来很容易实现,但它有明显的缺点。为每个请求创建一个新线程需要更多的时间,创建和销毁线程需要更多的系统资源。所以同时创建太多线程的JVM可能会导致系统内存不足,这就需要限制要创建的线程数量,也就是使用线程池。
一、Java中的线程池是什么?
线程池技术(Thread pool technology)是线程的重用技术,它使用先前创建的线程来执行当前任务,并为线程周期开销和资源冲突问题提供解决方案。因为请求到达时线程已经存在,所以消除了线程创建过程造成的延迟,应用程序获得了更快的响应。
Java提供了一个以executor接口及其子接口ExecutorService和ThreadPoolExecutor为中心的Executor框架。通过使用执行器,完成线程任务,只需要实现Runnable接口,交给执行器执行即可。
为你封装线程池,让你的编程任务专注于具体任务的实现,而不是线程的实现机制。
为了使用线程池,我们首先创建一个ExecutorService对象,然后向它传递一组任务。ThreadPoolExcutor类可以设置线程池的初始化和最大线程容量。
上图显示线程池初始化中有三个线程,任务队列中有五个任务对象要运行。
执行器线程池方法
方法
形容
newFixedThreadPool(int)
创建一个线程数量固定的线程池,int参数表示线程池中的线程数量。
newCachedThreadPool()
创建一个可缓存的线程池,可以灵活回收空闲线程。如果没有空闲线程,则创建一个新的线程处理任务。
newSingleThreadExecutor()
创建单线程线程池,该线程池将只使用唯一的工作线程来执行任务。
新计划线程池
创建一个固定长度的线程池来支持计划的和定期的任务执行。
在固定线程池的情况下,如果所有当前正在运行的线程都被执行,那么挂起的任务将被放入队列中,并在线程空闲时执行。
二。线程池示例
在下面的内容中,我们将介绍线程池的执行器。
创建线程池处理任务要遵循的步骤
创建一个任务对象(实现Runnable接口)来执行特定的任务逻辑。
使用执行器创建线程池执行器服务
将要执行的任务对象交给ExecutorService进行任务处理。
停止执行器线程池
//步骤1:创建一个任务对象(实现Runnable接口)来执行特定的任务逻辑(步骤1)
类任务实现Runnable {
私有字符串名称;
公共任务(字符串){
name=s;
}
//打印任务名称并休眠1秒钟
//整个过程执行5次。
公共无效运行(){
尝试{
for(int I=0;I=5;i ) {
if (i==0) {
日期d=新日期();
simple date format ft=new simple date format(' hh:mm:ss ');
System.out.println('任务初始化' name '=' ft . format(d));
//第一次执行时打印每个任务的名称和初始化时间。
}
否则{
日期d=新日期();
simple date format ft=new simple date format(' hh:mm:ss ');
System.out.println ('task正在执行' name '=' ft . format(d));
//打印每个任务处理的执行时间。
}
thread . sleep(1000);
}
System.out.println('任务执行完成'名称);
} catch(InterruptedException e) {
e . printstacktrace();
}
}
}
判例案件
公共类ThreadPoolTest {
//线程池中的最大线程数
static final int MAX _ SIZE=3;
公共静态void main (String[] args) {
//创建5个任务
Runnable r1=新任务('任务1 ');
Runnable r2=新任务('任务2 ');
Runnable r3=新任务('任务3 ');
Runnable r4=新任务('任务4 ');
Runnable r5=新任务('任务5 ');
//第二步:创建一个线程数量固定的线程池,线程数量为MAX_SIZE
ExecutorService pool=executors . newfixedthreadpool(MAX _ SIZE);
//第三步:将要执行的任务对象交给ExecutorService进行任务处理。
pool . execute(R1);
pool . execute(R2);
pool . execute(R3);
pool . execute(R4);
pool . execute(r5);
//步骤4:关闭线程池
pool . shut down();
}
}
示例执行结果
初始化任务任务1=05:25:55
初始化任务任务2=05:25:55
任务初始化任务3=05:25:55
正在执行任务任务3=05:25:56
任务正在执行任务1=05:25:56
任务正在执行任务2=05:25:56
该任务正在执行任务1=05:25:57
正在执行任务任务3=05:25:57
任务正在执行任务2=05:25:57
正在执行任务任务3=05:25:58
该任务正在执行任务1=05:25:58
任务正在执行任务2=05:25:58
该任务正在执行任务2=05:25:59
正在执行任务任务3=05:25:59
任务正在执行任务1=05:25:59
任务正在执行任务1=05:26:00
任务正在执行任务2=05:26:00
任务正在执行任务3=05:26:00
任务执行已完成任务3
任务执行已完成任务2
任务执行已完成任务1
初始化任务任务5=05:26:01
初始化任务任务4=05:26:01
任务正在执行任务4=05:26:02
任务正在执行任务5=05:26:02
任务正在执行任务4=05:26:03
任务正在执行任务5=05:26:03
任务正在执行任务5=05:26:04
任务正在执行任务4=05:26:04
任务正在执行任务4=05:26:05
任务正在执行任务5=05:26:05
任务正在执行任务4=05:26:06
任务正在执行任务5=05:26:06
任务执行已完成任务4
任务执行已完成任务5
如程序执行结果所示,只有当池中的线程空闲时,才会执行任务4或任务5。在此之前,其他任务将被放在队列中等待执行。
线程池执行前三个任务,线程池中的线程被回收,然后被处理执行任务4和5。
使用这种线程池方法的一个主要优点是,如果你想一次处理10,000个请求,但又不想创建10,000个线程,这样可以避免过度使用系统资源导致的停机。您可以使用这个方法创建一个包含500个线程的线程池,并向线程池提交500个请求。
ThreadPool将创建多达500个线程,一次处理500个请求。任何线程的进程完成后,ThreadPool将在内部将第501个请求分配给该线程,并继续对所有剩余的请求执行相同的操作。在系统资源紧张的情况下,线程池是保证程序稳定运行的有效解决方案。
三、线程池的使用注意事项和调优
死锁(Deadlock ):虽然任何多线程程序中都可能发生死锁,但线程池会引入另一种死锁情况,即所有正在执行的线程都在等待队列中某个被阻塞线程的执行结果,导致该线程无法继续执行。
线程泄漏:如果任务完成时线程池中的线程没有正确返回,就会出现线程泄漏问题。例如,如果一个线程抛出了一个异常,而池类未能捕捉到它,那么该线程将异常退出,线程池的大小将减少一。如果这种情况重复多次,线程池最终会变空,没有线程可用于执行其他任务。
频繁的线程轮换:如果线程池非常大,在线程之间切换上下文会浪费很多时间。所以在系统资源允许的情况下,线程池越大越好。
线程池大小优化:线程池的最佳大小取决于可用处理器的数量和挂起任务的性质。对于CPU密集型任务,假设系统有N个逻辑处理核心,最大数量为N或N-1的线程池将实现最高效率。对于I/O密集型任务,应该考虑请求的等待时间(W)与服务的处理时间(S)的比值,N*(1 W/S)的最大线程池大小将实现最高的效率。
不要教条式的使用上面的总结,需要根据自己的应用任务处理类型进行灵活的设置和调优,其中测试实验必不可少。
原文链接:字母表博客。
以上是Java线程池的作用和使用方法的详细介绍。关于Java线程池的作用和使用的更多信息,请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。