广州做企业网站找哪家公司好,python网站开发教程,安徽招标投标信息网,网站如何合理建设seo更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验
线程回顾 创建线程的方式
继承 Thread 类实现 Runnable 接口
创建后的线程有如下状态#xff1a;
NEW#xff1a;新建的线程#xff0c;无任何操作
public static void main(String[] args) {Thread… 更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验
线程回顾 创建线程的方式
继承 Thread 类实现 Runnable 接口
创建后的线程有如下状态
NEW新建的线程无任何操作
public static void main(String[] args) {Thread thread new Thread(() - System.out.println(Thread.currentThread().getName() is running));Thread.State state thread.getState();// 线程刚创建还未执行状态为 NEWSystem.out.println(state);}RUNNABLE可执行的线程在 JVM 中执行但是在等待操作系统的资源
public static void main(String[] args) {Thread thread new Thread(() - System.out.println(Thread.currentThread().getName() is running));thread.start();Thread.State state thread.getState();// 调用 start() 方法,可以执行但不代表一定在执行System.out.println(state);
}BOLCKED阻塞获取不到锁
public static void main(String[] args) {Thread thread1 new Thread(() - {synchronized (Test.class) {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 new Thread(() - {synchronized (Test.class) {}});thread1.start();thread2.start();// 等待保证 线程1 开始执行try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Thread.State state thread2.getState();System.out.println(state);}WAITING等待等待其他线程进行操作时间不确定
public static void main(String[] args) {Thread thread new Thread(LockSupport::park);thread.start();// 等待线程开始执行try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}Thread.State state thread.getState();System.out.println(state);// 等待状态解除禁止线程执行完毕LockSupport.unpark(thread);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}state thread.getState();System.out.println(state);}TIMED_WAITING等待等待的时间是确定的
public static void main(String[] args) {Thread thread new Thread(() - {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}});thread.start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}Thread.State state thread.getState();System.out.println(state);}TERMINATED终止线程已经运行完毕
public static void main(String[] args) {Thread thread new Thread(() - System.out.println(Thread.currentThread().getName() is running));thread.start();try {// 等待线程执行完毕Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Thread.State state thread.getState();System.out.println(state);}引入线程池 上述创建线程的方式存在如下缺陷
线程使用完后会被销毁高并发场景下频繁创建和销毁线程的性能开销不可忽略无法控制线程并发数量线程过多会导致 JVM 宕机
线程池是一种池化思想由于创建和销毁线程需要时间以及系统资源开销我们需要一个“管理者来统一管理线程及任务分配减少这些开销解决资源不足的问题。
在主要大厂的编程规范中不允许在应用中自行显式地创建线程线程必须通过线程池提供。 线程池解决了什么问题 降低系统资源消耗通过重用已存在的线程降低线程创建和销毁造成的消耗提高系统响应速度当有任务到达时通过复用已存在的线程无需等待新线程的创建便能立即执行方便线程并发数的管控。因为线程若是无限制的创建可能会导致内存占用过多而产生OOM节省CPU切换线程的时间成本需要保持当前执行线程的现场并恢复要执行线程的现场。提供更强大的功能延时定时线程池 线程池引发了什么问题 异步任务提交后如果JVM宕机已提交的任务会丢失需要考虑确认机制使用不合理可能导致内存溢出问题参数过多代码结构引入数据结构与算法增加使用难度 线程池概述 线程池继承结构 最常用的是 ThreadPoolExecutor调度用 ScheduledThreadPoolExecutor类似 Timer 和 TimerTask。任务拆分合并用 ForkJoinPoolExecutors是工具类协助创建线程池的 线程池工作状态 RUNNING运行状态)这是线程池的初始状态。 在此状态下线程池接受新任务并且也会处理等待队列中的任务。线程池的线程会一直运行直到被转换到其他状态。 SHUTDOWN关闭状态当调用线程池的shutdown()方法时线程池会进入此状态。 在此状态下线程池不接受新任务但会继续处理等待队列中的任务。等待队列中的任务处理完毕后线程池中的线程会逐渐结束直到所有线程结束运行。 STOP停止状态当调用线程池的shutdownNow()方法时线程池会进入此状态。 在此状态下线程池不接受新任务同时也不处理等待队列中的任务而是尝试停止所有正在执行的任务并且回收线程池中的所有线程。 TIDYING整理状态当所有的任务已经终止workerCount工作线程数为0时线程池会进入此状态。 此时会执行terminated()钩子方法允许线程池执行一些收尾工作。 TERMINATED终止状态terminated()钩子方法执行完毕后线程池会进入此状态。 在终止状态下线程池的任务完全结束不再有任何活动。 七个核心参数 参数名描述corePoolSize核心线程池基本大小核心线程数maximumPoolSize线程池最大线程数keepAliveTime线程空闲后的存活时间TimeUnit unit线程空闲后的存活时间单位BlockingQueue workQueue存放任务的阻塞队列ThreadFactory threadFactory创建线程的工厂RejectedExecutionHandler handler当阻塞队列和最大线程池都满了之后的饱和策略 corePoolSize 核心线程数
线程池刚创建时线程数量为0当每次执行 execute 添加新的任务时会在线程池创建一个新的线 程直到线程数量达到 corePoolSize 为止。核心线程会一直存活即使没有任务需要执行当线程数小于核心线程数时即使有线程空闲线程 池也会优先创建新线程处理设置 allowCoreThreadTimeouttrue 默认false时核心线程超时会关闭 maximumPoolSize 最大线程数
当池中的线程数 corePoolSize 且任务队列已满时。线程池会创建新线程来处理任务当池中的线程数 maximumPoolSize 且任务队列已满时线程池会拒绝处理任务而抛出异常
如果使用无界的阻塞队列该参数不生效 BlockingQueue 阻塞队列
当线程池正在运行的线程数量已经达到 corePoolSize 那么再通过 execute 添加新的任务则会被加到 workQueue 队列中任务会在队列中排队等待执行而不会立即执行一般来说这里的阻塞队列有以下几种选择ArrayBlockingQueue LinkedBlockingQueue SynchronousQueue keepAliveTime TimeUnit 保活时间及其单位
当线程空闲时间达到 keepAliveTime 时线程会退出直到线程数量 corePoolSize如果 allowCoreThreadTimeouttrue 则会直到线程数量0
keepAliveTime 是时间的大小TimeUnit 是时间单位 ThreadFactory 线程工厂
主要用来创建线程通过newThread()方法提供创建线程该方法创建的线程都是“非守护线程”而且“线程优先级都是默认优先级” RejectedExecutionHandler 拒绝策略
当线程数已经达到 maxPoolSize 且队列已满会拒绝新任务当线程池被调用 shutdown() 后会等待线程池里的任务执行完毕再 shutdown 。如果在调用shutdown() 和线程池真正 shutdown 之间提交任务会拒绝新任务当拒绝处理任务时线程池会调用 rejectedExecutionHandler 来处理这个任务。
如果没有设置默认是 AbortPolicy 另外在 ThreadPoolExecutor 类有几个内部实现类来处理这类情况
ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出 RejectedExecutionException 异常。ThreadPoolExecutor.CallerRunsPolicy 由调用线程处理该任务ThreadPoolExecutor.DiscardPolicy 也是丢弃任务但是不抛出异常ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列最前面的任务然后重新尝试执行任务
另外实现 RejectedExecutionHandler 接口即可实现自定义的拒绝策略 线程池逻辑结构 线程池的编程模式下任务是提交给整个线程池而不是直接提交给某个线程线程池在拿到任务后就在内部协调空闲的线程如果有则将任务交给某个空闲的华线程。
一个线程同时只能执行一个任务但可以同时向一个线程池提交多个任务。
当一个任务被提交线程池会进行如下工作 首先判断当前的核心线程数量如果小于核心线程数创建一个核心线程并执行任务 如果大于核心线程数则尝试将其放入等待队列如果队列没有满则放入队列等待执行 如果队列已满则判断非核心线程数的数量核心线程数是否小于最大线程数量 小于则创建一个非核心线程并执行任务并不会取队列中的任务 大于执行拒绝策略 线程池线程数设置 虽然使用线程池的好处很多但是如果其线程数配置得不合理不仅可能达不到预期效果反而可能降低应用的性能。
因此按照任务类型分类对不同的任务类型确定不同的线程数量。 任务类型分类 IO密集型任务 此类任务主要是执行 IO 操作。由于执行 lO 操作的时间较长导致 CPU 的利用率不高这类任务 CPU 常处于空闲状态。Netty 的 IO 读写操作为此类任务的典型例子 CPU 密集型任务 此类任务主要是执行计算任务。由于响应时间很快CPU 一直在运行这种任务 CPU 的利用率很高。例如设计加密解密算法等大量需要 CPU 运算的场景 混合型任务 此类任务既要执行逻辑计算又要进行 IO 操作如 RPC 调用、数据库访问相对来说由于执行 IO 操作的耗时较长一次网络往返往往在数百毫秒级别)这类任务的 CPU 利用率也不是太高。Web 服务器的 HTTP 请求处理操作为此类任务的典型例子 确定任务线程数 IO 密集型任务 由于 IO 密集型任务的CPU使用率较低导致线程空余时间很多因此通常需要开 CPU 核心数两倍的线程Netty 的 IO 处理任务就是典型的 IO 密集型任务所以Netty 的 Reactor 实现类定制版的线程池的 IO 处理线程数默认正好为 CPU 核数的两倍 CPU密集型任务 CPU 密集型的任务并行的任务越多花在任务切换的时间就越多CPU 执行任务的效率就越低一般开等于 CPU 的核心数的线程数量比如 4 个核心的 CPU通过 4 个线程并行地执行 4 个 CPU 密集型任务此时的效率是最高的。但是如果线程数远远超出 CPU 核心数量就需要频繁地切换线程线程上下文切换时需要消耗时间反而会使得任务效率下降。 混合型任务 业界有一个比较成熟的估算公式具体如下 最佳线程数 ((线程等待时间 线程 CPU 时间) / 线程 CPU 时间) * CPU核数通过公式可以看出等待时间所占的比例越高需要的线程就越多CPU 耗时所占的比例越高需要的线程就越少 比如在 Web 服务器处理 HTTP 请求时假设平均线程 CPU 运行时间为 100 毫秒而线程等待时间比如包括 DB 操作、RPC 操作作、缓存操作等为 900 毫秒如果 CPU 核数为 8 那么根据上面这个公式估算如下(900 毫秒100 毫秒) / 100 毫秒 * 8 10 * 8 80最好开 80 个线程