dede网站地图插件,有免费网站推荐吗,中资源 网站域名解析,公司如何做自己的网站我是一个计算机专业研0的学生卡蒙Camel#x1f42b;#x1f42b;#x1f42b;#xff08;刚保研#xff09;
记录每天学习过程#xff08;主要学习Java、python、人工智能#xff09;#xff0c;总结知识点#xff08;内容来自#xff1a;自我总结网上借鉴#xff0…我是一个计算机专业研0的学生卡蒙Camel刚保研
记录每天学习过程主要学习Java、python、人工智能总结知识点内容来自自我总结网上借鉴
希望大家能一起发现问题和补充也欢迎讨论文章目录 线程池线程池的好处线程池的创建️线程池(ThreadPoolExecutor)常见参数处理任务流程拒绝策略⭐使用数据库任务表来自定义拒绝策略 线程池中两种提交方式线程池命名♂️♀️线程池状态 线程池
线程池是多线程应用中的一种资源管理技术它旨在减少创建和销毁线程所带来的开销并且通过复用已存在的线程来执行任务提高响应速度。线程池提供了一种限制和管理资源包括执行一个任务的线程的方法。
线程池的好处
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。
线程池的创建️
使用ThreadPoolExecutor构造函数来创建自定义线程池建议这种方式这样的处理方式让写的同学更加明确线程池的运行规则规避资源耗尽的风险通过使用 java.util.concurrent.Executors 工厂类创建
常用的线程池
Executors 返回线程池对象特点FixedThreadPool创建一个固定大小的线程池。如果所有线程都处于活动状态新任务将在队列中等待直到有线程可用。CachedThreadPool创建一个可根据需要创建新线程的线程池但在前一次构造的线程可用时将重用它们。适用于执行大量短期异步任务的应用程序。SingleThreadExecutor创建一个单线程化的 Executor它会确保所有任务都在同一个线程中按顺序执行。ScheduledThreadPool创建一个支持定时及周期性的任务执行的线程池类似于 Timer。WorkStealingPool创建一个具有多个任务队列的工作窃取线程池适用于处理大量可并行的任务。
线程池对象的构造方法
// LinkedBlockingQueue有界队列
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable());
}// LinkedBlockingQueue 无界队列
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueueRunnable()));
}// SynchronousQueue同步队列
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueueRunnable());
}// DelayedWorkQueue延迟阻塞队列
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);
}
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}...
}线程池(ThreadPoolExecutor)常见参数
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize 0 ||maximumPoolSize 0 ||maximumPoolSize corePoolSize ||keepAliveTime 0)throw new IllegalArgumentException();if (workQueue null || threadFactory null || handler null)throw new NullPointerException();this.acc System.getSecurityManager() null ?null :AccessController.getContext();this.corePoolSize corePoolSize;this.maximumPoolSize maximumPoolSize;this.workQueue workQueue;this.keepAliveTime unit.toNanos(keepAliveTime);this.threadFactory threadFactory;this.handler handler;
}参数解释
参数解释corePoolSize⭐线程池的核心线程数量任务队列未达到队列容量时最大可以同时运行的线程数量。maximumPoolSize⭐线程池的最大线程数任务队列中存放的任务达到队列容量的时候当前可以同时运行的线程数量变为最大线程数。keepAliveTime当线程数大于核心线程数时多余的空闲线程存活的最长时间unit时间单位使用java.util.concurrent.TimeUnit枚举值来指定时间单位workQueue⭐任务队列用来储存等待执行任务的队列threadFactory线程工厂用来创建线程一般默认即可handler拒绝策略当提交的任务过多而不能及时处理时我们可以定制策略来处理任务常见的拒绝策略包括抛出异常、丢弃任务、执行者自身运行任务等。
工作队列workQueue用于存放待执行的任务的阻塞队列。可以使用BlockingQueue接口的任何实现常见的有
ArrayBlockingQueue一个由数组结构组成的有界阻塞队列。LinkedBlockingQueue一个基于链表结构的有界阻塞队列。如果构造时未指定容量则默认容量为Integer.MAX_VALUE即视为无界队列。SynchronousQueue不存储元素的阻塞队列每个插入操作必须等待另一个线程的对应移除操作。PriorityBlockingQueue一个支持优先级排序的无界阻塞队列。**LinkedBlockingDeque**一个由链表结构组成的双端阻塞队列。 处理任务流程
ThreadPoolExecutor中最关键的execute源码
public void execute(Runnable command) {// 先检查传入的command是否为空若空则报错if (command null)throw new NullPointerException();int c ctl.get(); // 原子操作返回线程池状态和线程计数的控制字段值// 1. 如果线程数少于corePoolSize调用addWorker创建核心线程数if (workerCountOf(c) corePoolSize) {if (addWorker(command, true))return;c ctl.get();}// 2. 如果线程数大于corePoolSize将任务添加进workQueue队列if (isRunning(c) workQueue.offer(command)) {int recheck ctl.get();// 2.1 如果检查isRunning的状态为false则remove这个任务然后执行拒绝策略if (! isRunning(recheck) remove(command))reject(command);// 2.2 线程池处于running状态但是没有线程则创建线程else if (workerCountOf(recheck) 0)addWorker(null, false);}// 3.如果放入workQueue失败则创建非核心线程执行任务如果创建失败(当前线程总数不小于maximumPoolSize)就会拒绝else if (!addWorker(command, false))reject(command);
}拒绝策略⭐
在Java的ThreadPoolExecutor中当线程池无法处理新提交的任务时例如线程池已关闭或者队列已满可以配置不同的拒绝策略来决定如何处理这些任务。
拒绝策略详细ThreadPoolExecutor.AbortPolicy**默认的拒绝策略。**抛出 RejectedExecutionException来拒绝新任务的处理。ThreadPoolExecutor.CallerRunsPolicy如果使用这种策略那么当任务被拒绝时该任务将在调用者的线程中执行即直接在调用execute方法的线程中运行这个任务。这可能会降低任务提交线程的速度从而减缓任务提交的速度给线程池一些时间来处理队列中的任务。ThreadPoolExecutor.DiscardPolicy该策略会悄悄地丢弃无法处理的任务既不会抛出异常也不会通知调用者。因此使用此策略时需要注意可能丢失任务的情况。ThreadPoolExecutor.DiscardOldestPolicy此策略会丢弃队列中最旧的一个未处理任务并尝试重新提交当前任务。这意味着最老的任务将从队列中移除然后尝试将当前任务添加到队列中进行处理。自定义拒绝策略通过实现接口可以自定义任务拒绝策略。
在日常开发中我们不希望任务被丢弃我们就会使用CallerRunsPolicy拒绝策略。
// 被拒绝任务的处理程序直接在execute方法的调用线程中运行被拒绝的任务除非执行器已关闭在这种情况下任务将被丢弃。
public static class CallerRunsPolicy implements RejectedExecutionHandler {/*** Creates a {code CallerRunsPolicy}.*/public CallerRunsPolicy() { }/***在调用者的线程中执行任务r除非执行器已关闭在这种情况下任务将被丢弃* param r 请求执行的可运行任务* param e 试图执行此任务的执行器*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}
}又源代码可知只要当前程序不关闭就会使用执行execute方法的线程执行该任务。
但是会有一定风险
如果走到CallerRunsPolicy的任务是个非常耗时的任务且处理提交任务的线程是主线程可能会导致主线程阻塞影响程序的正常运行。也可能会造成死锁(如果调用者线程正在等待线程池中的某个任务完成而这个任务又依赖于调用者线程能够继续运行那么就会形成循环依赖进而导致死锁。)
解决方案 继续采用CallerRunsPolicy拒绝策略 在内存允许的情况下我们可以增加阻塞队列BlockingQueue的大小并调整堆内存以容纳更多的任务确保任务能够被准确执行。 1为了充分利用 CPU我们还可以调整线程池的maximumPoolSize 最大线程数参数这样可以提高任务处理速度避免累计在 BlockingQueue的任务过多导致内存用完。 持久化思路自定义 设计一个数据库表来存储到数据库中 使用Redis缓存 提交到消息队列
使用数据库任务表来自定义拒绝策略
实现RejectedExecutionHandler接口自定义拒绝策略自定义拒绝策略负责将线程池暂时无法处理此时阻塞队列已满的任务入库保存到 MySQL 中。注意线程池暂时无法处理的任务会先被放在阻塞队列中阻塞队列满了才会触发拒绝策略。
public class DatabaseRejectedExecutionHandler implements RejectedExecutionHandler {Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 保存任务到数据库的方法saveTaskToDatabase(r);}private void saveTaskToDatabase(Runnable task) {// 实现保存任务到数据库的逻辑// 可以使用JDBC或者其他ORM框架如MyBatis、Hibernate等}
}继承BlockingQueue实现一个混合式阻塞队列该队列包含 JDK 自带的ArrayBlockingQueue。另外该混合式阻塞队列需要修改取任务处理的逻辑也就是重写take()方法取任务时优先从数据库中读取最早的任务数据库中无任务时再从 ArrayBlockingQueue中去取任务。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class HybridBlockingQueueE implements BlockingQueueE {private final ArrayBlockingQueueE queue;public HybridBlockingQueue(int capacity) {this.queue new ArrayBlockingQueue(capacity);}// 其他BlockingQueue接口方法的实现...Overridepublic E take() throws InterruptedException {// 尝试从数据库中取任务E taskFromDb takeFromDatabase();if (taskFromDb ! null) {return taskFromDb;} else {// 如果数据库中没有任务则从queue中取return queue.take();}}private E takeFromDatabase() {// 实现从数据库中取任务的逻辑// 注意这里需要根据你的E类型来确定如何反序列化或转换成任务对象return null; // 返回null表示数据库中没有任务}Overridepublic boolean offer(E e) {return queue.offer(e);}Overridepublic int remainingCapacity() {return queue.remainingCapacity();}// ...其他必须实现的方法例如put(), poll(), etc.
}线程池中两种提交方式
execute方法
void execute(Runnable command): Executor接口中的方
submit方法
T FutureT submit(CallableT task);
T FutureT submit(Runnable task, T result);
Future? submit(Runnable task);
两个方法区别 异常处理 使用execute()时未捕获异常导致线程终止线程池创建新线程替代 使用submit()时异常被封装在Future中线程继续复用。更加灵活的错误处理机制允许调用者决定如何处理异常 接受参数 返回值
线程池命名♂️♀️
给线程池命名可以帮助你在调试和监控多线程应用程序时更容易识别不同的线程池。默认情况下创建的线程名字类似 pool-1-thread-n 这样的没有业务含义不利于我们定位问题。
使用ThreadFactory来创建一个带有自定义名称的线程池
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;/*** 线程工厂它设置线程名称有利于我们定位问题。*/
public final class NamingThreadFactory implements ThreadFactory {private final AtomicInteger threadNum new AtomicInteger();private final String name;/*** 创建一个带名字的线程池生产工厂*/public NamingThreadFactory(String name) {this.name name;}Overridepublic Thread newThread(Runnable r) {Thread t new Thread(r);t.setName(name [# threadNum.incrementAndGet() ]);return t;}
}线程池状态
线程池有5种状态
// runState is stored in the high-order bits
private static final int RUNNING -1 COUNT_BITS;
private static final int SHUTDOWN 0 COUNT_BITS;
private static final int STOP 1 COUNT_BITS;
private static final int TIDYING 2 COUNT_BITS;
private static final int TERMINATED 3 COUNT_BITS;线程池状态详细介绍RUNNING表示线程池处于正常运行状态可以接受新的任务并处理已提交的任务。SHUTDOWN表示线程池不再接受新任务但会继续执行已经提交的任务直到所有任务完成。通过调用 shutdown() 方法进入此状态。STOP表示线程池不再接受新任务并尝试停止正在执行的所有任务。通过调用 shutdownNow() 方法进入此状态。TIDYING表示所有任务都已完成工作线程数量为零即将进入 TERMINATED 状态。这是一个短暂的过渡状态在这个状态下terminated() 钩子方法会被调用。TERMINATED表示线程池完全终止所有的任务都已完成并且所有的工作线程都已被销毁。 shutdownNow为STOP并试图停止所有正在执行的线程不再处理还在池队列中等待的任务当然它会返回那些未执行的任务。它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的但是这种方法的作用有限如果线程中没有sleep、wait、Condition、定时锁等应用,interrupt()方法是无法中断当前的线程的。所以shutdownNow()并不代表线程池就一定立即就能退出它可能必须要等待所有正在执行的任务都执行完成了才能退出。