本文主要是介绍JUC并发编程-浅学浅用之线程池,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
11. 线程池(重点)
重点理解
线程池:三大方式、七大参数、四种拒绝策略,还有如何设置最大线程池的大小
池化技术
程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术
线程池、JDBC的连接池、内存池、对象池 等等。。。。
资源的创建、销毁十分消耗资源
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。
1)线程池的好处:
1、降低资源的消耗;
2、提高响应的速度;
3、方便管理;
2)线程池:三大方法(工具类)
- ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
- ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小
- ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的
不建议使用工具类,不允许使用Executors去创建,建议使用ThreadPoolExecutor方式创建
//工具类 Executors 三大方法;
public class Demo01 {public static void main(String[] args) {ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的//线程池用完必须要关闭线程池try {for (int i = 1; i <=100 ; i++) {//通过线程池创建线程threadPool.execute(()->{System.out.println(Thread.currentThread().getName()+ " ok");});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}
3)七大参数
public ThreadPoolExecutor(int corePoolSize, //核心线程池大小int maximumPoolSize, //最大的线程池大小long keepAliveTime, //超时了没有人调用就会释放TimeUnit unit, //超时单位BlockingQueue<Runnable> 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.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}
阿里巴巴的Java操作手册中明确说明:对于Integer.MAX_VALUE初始值较大,所以一般情况我们要使用底层的ThreadPoolExecutor来创建线程池
public class PollDemo {public static void main(String[] args) {// 获取cpu 的核数int max = Runtime.getRuntime().availableProcessors();ExecutorService service =new ThreadPoolExecutor(2,max,3,TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());try {for (int i = 1; i <= 10; i++) {service.execute(() -> {System.out.println(Thread.currentThread().getName() + "ok");});}}catch (Exception e) {e.printStackTrace();}finally {service.shutdown();}}
}
4)拒绝策略
new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常
超出最大承载,就会抛出异常:队列容量大小+maxPoolSize
new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理
new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常
AbortPolicy()
CallerRunsPolicy()
DiscardPolicy()
DiscardOldestPolicy()
5)如何设置线程池的大小
1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小
// 获取cpu 的核数int max = Runtime.getRuntime().availableProcessors();ExecutorService service =new ThreadPoolExecutor(2,max,3,TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
2、I/O密集型:
在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。
6)线程池demo
下面是一个使用ThreadPoolExecutor创建线程池的例子,涉及线程池的多种配置:
import java.util.concurrent.*;public class ThreadPoolExample {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数4, // 最大线程数10, // 空闲线程存活时间TimeUnit.SECONDS, // 存活时间的单位new ArrayBlockingQueue<>(5), // 任务队列Executors.defaultThreadFactory(), // 线程工厂new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);// 提交任务给线程池for (int i = 0; i < 10; i++) {final int taskId = i;executor.submit(() -> {System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}// 关闭线程池executor.shutdown();}
}
上述例子中,使用ThreadPoolExecutor创建了一个线程池,参数配置如下:
1. 核心线程数:2,线程池会一直保持这些线程,即使它们是空闲的。
2. 最大线程数:4,线程池中最多同时执行的线程数量。
3. 空闲线程存活时间:10秒,如果线程池中的线程空闲时间超过该值,且当前线程数大于核心线程数,那么这个线程会被销毁。
4. 存活时间的单位:秒。
5. 任务队列:ArrayBlockingQueue,最多容纳5个任务的阻塞队列。
6. 线程工厂:Executors.defaultThreadFactory(),使用默认的线程工厂创建线程。
7. 拒绝策略:CallerRunsPolicy,当线程池无法执行新任务时,会将任务返回给调用者来执行。
然后,通过循环提交10个任务给线程池,并在每个任务中输出当前任务的ID和执行线程的名称。最后,调用shutdown()方法关闭线程池。
拓展:CPU密集型和IO密集型理解
CPU密集型(CPU-bound)
CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。
**在多重程序系统中,大部份时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。**例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部份时间用在三角函数和开根号的计算,便是属于CPU bound的程序。
**CPU bound的程序一般而言CPU占用率相当高。**这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。
IO密集型(I/O bound)
IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。
**I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。**这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。
CPU密集型 vs IO密集型
我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低
,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。 对于计算密集型任务,最好用C语言编写。
第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
总之,计算密集型程序适合C语言多线程,I/O密集型适合脚本语言开发的多线程。
JUC并发编程-浅学浅用之线程池 到此完结,笔者归纳、创作不易,大佬们给个3连再起飞吧
这篇关于JUC并发编程-浅学浅用之线程池的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!