昆山 网站建设,小学生手工,网站的优化和推广方案怎么写,嘉兴做网站设计目录
前言
什么是线程池
线程池的优点
ThreadPollExecutor中的构造方法
corePoolSize maximumPoolSize
keepAliveTime unit
workQueue threadFactory
如何在java中使用线程池
1.创建线程池对象 2.调用submit添加任务
3.调用shutdown关闭线程池… 目录
前言
什么是线程池
线程池的优点
ThreadPollExecutor中的构造方法
corePoolSize maximumPoolSize
keepAliveTime unit
workQueue threadFactory
如何在java中使用线程池
1.创建线程池对象 2.调用submit添加任务
3.调用shutdown关闭线程池
手动实现线程池
创建一个线程池类
测试 前言
在前面我们都是通过new Thread() 来创建线程的虽然在java中对线程的创建、中断、销毁、等值等功能提供了支持但从操作系统角度来看频繁的创建和销毁线程是需要大量的时间和资源的。那么我们能不能先把线程提前从系统中申请好需要使用线程的时候直接从这个地方取出来而不是从系统中重新申请等线程用完之后也是放回到这个地方而不用频繁的进行创建和销毁呢那我们就需要用到线程池线程池能够提高程序的效率。
什么是线程池
线程池 (ThreadPool) 是一种多线程处理形式处理过程中将任务添加到队列然后在创建后保持活动的线程中执行这些任务。线程池的主要目的是减少创建和销毁线程的开销提高响应速度和整体性能。
为什么说线程池里取线程比从系统中创建一个线程效率要高呢
我们从线程池里直接取出线程是纯用户态操作而我们新建一个线程是内核态用户态配合完成的。
内核态指操作系统内核及其相关组件如设备驱动程序运行的状态。
用户态指用户程序如应用程序和服务运行的状态。 操作系统操作系统内核操作系统配套的应用程序 假设现在要去银行办银行卡那么银行内部人员就是内核态前往办卡的就为用户态。 如果我们想要办卡就走到小窗口向柜员说明情况此时柜员就会帮你进行操作。但如果此过柜员问你要身份证复印件如果没有那么就需要去进行打印。
若请求柜员帮你进行打印那么柜员什么时候打印这个是不确定的效率较低。这种就是用户态内核态调用系统API由系统内核来完成这一系列操作。
若我们自己去自助复印机进行复印整个过程就是连贯可控的效率较高。这种就是纯用户态由我们自己控制。
线程池的优点
减少资源消耗通过重复利用已创建的线程减少了创建和销毁线程所需的资源开销。提高响应速度当有新的任务到达时若线程池中有空闲的线程任务可以直接执行无需等待新的线程创建。防止资源耗尽通过限制线程池中的最大线程数可以避免因创建过多线程而引起的资源耗尽问题。提高线程利用率线程池中的线程可以被重复利用。
线程池最大的好处就是减少每次启动、销毁线程的损耗。
ThreadPollExecutor中的构造方法
在java中给我们提供了现成的线程池供我们来使用。
我们可以看一下标准库中的线程池在java.util.concurrent中给我们提供线程池的相关方法。
在点开concurrent之后我们在接口中往下划找到ThreadPoolExecutor类。我们可以看到ThreadPoolExecutor类的构造方法有着好几个参数那么这些参数都是什么意思呢
‘’ 我们拿第四个构造方法来看。 corePoolSize maximumPoolSize
corePoolSize即核心线程数
maximumPoolSize最大核心线程数
线程池可以支持“线程扩容”若某个线程池初始状态下有m个线程但m若不够用就会自动增加m的个数。
在java标准库的线程池中把线程分为两类核心线程和非核心线程。
核心线程可以理解为最少有多少个线程而非核心线程就是线程扩容新增的线程。
核心线程数会始终存在线程池内部而非核心线程在繁忙的时候就会被创建出来在空闲时就会把这些线程释放掉。 最大核心线程数核心线程数非核心线程数的最大值 keepAliveTime unit 那么非核心线程在空闲的时候就会被立即释放掉吗
若在空闲一会后又有新的任务那岂不是要重新新增线程。
所以在构造方法中给我们提供了两个参数用来设置非核心线程在空闲时等待任务的最长时间。
keepAliveTime表示线程池中空闲的非核心线程在终止前等待新任务的最长时间。
unit用来设置keepAliveTime参数的时间单位。
workQueue
我们可以看到workQueue的类型为BlockingQueueRunnable,说明这是个用来存放任务的阻塞队列。可以根据需要设置阻塞队列的类型如果需要优先级则可以使用PriorityBlockingQueue如果不需要优先级且任务的数目是恒定的则可以使用ArrayBlockingQueue如果任务的数目不是恒定的则可以使用LinkedBlockingQueue。
作用
任务缓存当线程池中的线程都在执行任务时新提交的任务会被暂时存储在工作队列中直到有空闲线程来执行它们。任务调度工作队列可以帮助线程池按照一定的策略来调度任务的执行顺序。资源管理通过限制工作队列的大小可以控制线程池中等待执行的任务数量从而避免资源耗尽的情况。 threadFactory threadFactory创建线程的⼯⼚,参与具体的创建线程⼯作.通过不同线程⼯⼚创建出的线程相当于 对⼀些属性进⾏了不同的初始化设置。
*handler(类型RejectedExecutionHandler
最后一个参数是有关队列满后不要进行阻塞而是要进行拒绝了我们可以在构造方法上面查看线程池的拒绝策略有哪些. 在java提供了4种拒绝策略
ThreadPoolExecutor.AbortPolicy有新的任务想要入队时直接抛出异常ThreadPoolExecutor.CallerRunsPolicy新添加的任务线程池拒绝执行由添加任务的线程执行该任务ThreadPoolExecutor.DiscardOldestPolicy丢弃掉最旧的未被处理的请求执行新的任务ThreadPoolExecutor.DiscardPolicy丢弃掉当前新加的任务不执行
讲完了线程池中的构造方法以及拒绝策略那么如何在java中来使用线程池呢
如何在java中使用线程池
1.创建线程池对象 ExecutorService exExecutors.newFixedThreadPool(4);ExecutorService是一个继承于Executor的接口主要用来管理和控制线程是Java并发编程的重要工具。 在java标准库中虽然ThreadPoolExecutor功能强大但是使用起来比较麻烦所以java标准库对这个类进行了封装Executors类 Executors类是Java中用于创建线程池的工厂类它提供了一系列的静态工厂方法用于创建不同类型的线程池。
我们可以看到Executors工厂类中的工厂方法有以下几种类型 newCachedThreadPool()创建一个可缓存的线程池。这个线程池的线程数量可以根据需要自动扩展如果有可用的空闲线程就会重用它们如果没有可用的线程就会创建一个新线程。适用于执行大量的短期异步任务。newFixedThreadPool(int nThreads)创建一个固定大小的线程池其中包含指定数量的线程。线程数量是固定的不会自动扩展。适用于执行固定数量的长期任务。newSingleThreadExecutor()创建一个单线程的线程池。这个线程池中只包含一个线程用于串行执行任务。适用于需要按顺序执行任务的场景。newScheduledThreadPool(int corePoolSize)创建一个固定大小的线程池用于定时执行任务。线程数量固定不会自动扩展此线程池可以安排在给定延迟后运行命令或者定期执行。newSingleThreadScheduledExecutor()创建一个单线程的定时执行线程池。只包含一个线程用于串行定时执行任务。newWorkStealingPool(int parallelism)创建一个工作窃取线程池线程数量根据CPU核心数动态调整。这种线程池适合处理大量细粒度的可取消任务。 2.调用submit添加任务
这里我们创建的是一个固定线程数为4的线程池在线程池中我们想要添加任务需要调用submit()方法可以看到需要的参数是一个Runnable类型的任务。 我们可以使用Lambda表达式或者直接new Runnable。这里用第一个 for(int i0;i100;i) {int id i;ex.submit(() - {System.out.println(Thread.currentThread().getName() 任务: id);});} 当我们运行之后会发现任务都结束后程序还没有停止这是为什么呢
在 Java 中线程池创建的线程默认为前台线程虽然main线程结束了但是线程池里的前台线程仍然存在。
3.调用shutdown关闭线程池
如果我们想要关闭线程池中的线程我们可以使用shutdown方法。 public static void main(String[] args) {ExecutorService exExecutors.newFixedThreadPool(4);for(int i0;i100;i) {int id i;ex.submit(() - {System.out.println(Thread.currentThread().getName() 任务: id);});}ex.shutdown();}
既然我们学会如何使用java中的线程池那么我们可以来实现一个线程池.
手动实现线程池
创建一个线程池类
由于这里是使用的阻塞队列在队列满的时候若添加新的任务会进入阻塞等待状态不同于java线程池里的拒绝策略。
/*** 自定义线程池执行器类* 该类通过实现一个具有固定大小的线程池和一个阻塞队列来管理线程用于异步执行任务*/
class MyThreadPoolExecutor {// 创建阻塞队列用于存放待执行的任务// 队列大小设为1000用于控制并发任务的数量避免过多任务导致资源耗尽BlockingQueueRunnable blockingQueuenew ArrayBlockingQueue(1000);/*** 构造函数初始化线程池* 创建一个线程该线程循环从阻塞队列中取任务并执行* 这个线程是线程池中的工作线程负责执行提交的任务*/public MyThreadPoolExecutor(int n) {for (int i 1; i n; i) {Thread t new Thread(() - {// 无限循环确保线程池可以持续处理任务直到程序中断或阻塞队列被清空while (true) {try {// 从阻塞队列中取出一个任务如果队列为空则线程被阻塞直到有任务放入队列Runnable task blockingQueue.take();// 执行取出的任务task.run();} catch (InterruptedException e) {// 如果线程在等待状态时被中断抛出运行时异常// 这通常会导致程序异常终止throw new RuntimeException(e);}}});// 启动线程池中的工作线程t.start();}}/*** 提交一个任务到线程池* param task 需要被执行的任务* 任务被放入阻塞队列中随后由线程池中的工作线程执行*/public void submit(Runnable task){// 将任务放入阻塞队列如果队列已满则操作会阻塞直到有空间可用blockingQueue.offer(task);}
}测试
class DemoTest1{public static void main(String[] args) throws InterruptedException {MyThreadPoolExecutor exnew MyThreadPoolExecutor(4);for(int i0;i100;i) {int id i;ex.submit(()-{System.out.println(Thread.currentThread().getName() 任务:id);});}}
}本篇就先到这了~ 若有不足欢迎指正~