java里线程池怎么定义的?什么样的结构?具体使用?
-
为什么要用线程池,直接一个一个的new线程不行吗?
-
主要是线程的资源对于系统来说是占用的资源比较多 比较重量级的一个资源
-
这类资源更倾向于个池化的线程池,直接维护好池化的线程池,用的时候从里面取
-
如果自己创建线程的话需要维护线程的多少 大小,因为物理资源是有限的
-
-
那么线程池是怎么处理大量过来的需要并行计算的业务呢?
-
首先要弄一个基础设施业务,线程池是很复杂的,好在jdk已经封装了一些api 包 jdk 已经实现了抽象的线程池的定义
jdk线程池:
1.Excutor:执行者顶层接口
void execute(Runnable command):执行可运行的任务
2.ExcutorService:接口api
继承了excutor 接口
添加了一些其他的方法
比如: void shutdown() 方法 :停止接受新的任务,原来任务继续执行
场景:优雅停机维护
List<Runnable> shutdownNow():停止接受新的任务 ,强制停止之前的任务
boolean awaitTermination(timeOut, unit):阻塞当前线程,返回是否线程都执行完
submit()方法
submit方法->有返回值,用Future封装
execute 方法->无返回值
submit 方法还异常可以在主线程中get 捕获到 (注意部分异常 比如算术异常 会被吞掉)
execute 方法执行任务是捕捉不到异常的
3.ThreadFactory:线程工厂(设计模式:工厂模式)
package java0.conc0302.threadpool; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * @author mcc * @title: ThreadFactoryDemo * @date 2022/5/221:02 */ public class ThreadFactoryDemo implements ThreadFactory { private AtomicInteger a=new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread T =new Thread(r); //a.getAndIncrement()) 先获取当前值在递增 T.setName("线程"+a.getAndIncrement()); return (T); } }
4.ThreadPoolExecutor:调用线程的参数 一般使用这个
-
判断corePoolSize(创建)
判断第一个运行线程数有没有达到 核心线程数 达不到新建一个出来
-
加入workQueue
如果达到!缓存队列排队 !
-
判断maximumPoolSize(创建)
如果缓存队列也满了,判断是否到达最大线程数
-
执行拒绝策略处理器
全满! 拒绝策略处理器
5.Excutors:工具类,创建线程
缓存队列:
BlockingQueue是双缓冲队列。BlockingQueue 允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。
-
ArrayBlockingQueue:规定大小的BlockingQueue,其构造必须指定大小。其所含的对象 是FIFO顺序排序的。
-
LinkedBlockingQueue:大小不固定的 BlockingQueue,若其构造时指定大小,生成的 BlockingQueue 有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是 FIFO顺序排序的。
-
PriorityBlockingQueue:类似于LinkedBlockingQueue,但是其所含对象的排序不是 FIFO, 而是依据对象的自然顺序或者构造函数的 Comparator(比较器 可以根据自己设定好的算法来处理相对应的顺序)决定。
4.SynchronizedQueue:特殊的 BlockingQueue,对其好澡作必须是放和取交替完成。(只能写入一个值)
线程池的参数
拒绝策略
-
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException异常 默认策略是这个
-
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
-
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝 的任务
-
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 现在基本上使用这个比较多 因为不会丢任务
这个的意思是 当前的线程池满了 ,然后还有大量的并发线程的任务 给 主线程,就是调用线程的那个人 ,他自己就会先自己去处理 ;缓解线程池的压力!!
-
-
创建线程池的方法:
除了一般使用这个之外ThreadPoolExecutor 。
Excutors工具类为我们提供多个简单的方法:
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因 为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
示例代码:
public class NewSingleThreadExecutorDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int no = i; executorService.execute(() -> { System.out.println("start:" + no); try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end:" + no); }); } executorService.shutdown(); System.out.println("Main Thread End!"); } }
start:0 Main Thread End! end:0 start:1 end:1 start:2 end:2 start:3 end:3 start:4 end:4 start:5 end:5 start:6 end:6 start:7 end:7 start:8 end:8 start:9 end:9
2.newFixedThreadPool 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最 大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NewFixedThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(16); for (int i = 0; i < 100; i++) { final int no = i; executorService.execute(() -> { try { System.out.println("start:" + no); Thread.sleep(1000L); System.out.println("end:" + no); } catch (InterruptedException e) { e.printStackTrace(); } }); } executorService.shutdown(); System.out.println("Main Thread End!"); } }
类似与jvm xmx xms 一样!
3.newCachedThreadPool
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class NewCachedThreadPoolDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10000; i++) { final int no = i; Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println("start:" + no); Thread.sleep(1000L); System.out.println("end:" + no); } catch (InterruptedException e) { e.printStackTrace(); } } }; executorService.execute(runnable); } executorService.shutdown(); System.out.println("Main Thread End!"); } }
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程, 那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会 对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM))能够创建的最大线程大小。 4..newScheduledThreadPool
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class NewScheduledThreadExecutorDemo { public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(16); for (int i = 0; i < 100; i++) { final int no = i; Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println("start:" + no); Thread.sleep(1000L); System.out.println("end:" + no); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 10s后执行 executorService.schedule(runnable, 10, TimeUnit.SECONDS); } executorService.shutdown(); System.out.println("Main Thread End!"); } }
创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。、
创建固定线程池的经验
不是越大越好,太小肯定也不好:假设核心数为N 1、如果是 CPU 密集型应用,则线程池大小设置为 N或 N+1 2、如果是IO密集型应用,则线程池大小设置为2N 或2N+2