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连再起飞吧

相关推荐

  1. 探索并发编程:深入理解线

    2024-02-02 12:56:01       40 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-02 12:56:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-02 12:56:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-02-02 12:56:01       82 阅读
  4. Python语言-面向对象

    2024-02-02 12:56:01       91 阅读

热门阅读

  1. HTML优化SEO的实用技巧

    2024-02-02 12:56:01       51 阅读
  2. undefined reference to symbol ‘pow@@GLIBC_2.0

    2024-02-02 12:56:01       57 阅读
  3. 持续积累分享金融知识

    2024-02-02 12:56:01       45 阅读
  4. SparkSql Join Types详解

    2024-02-02 12:56:01       50 阅读
  5. C++入门学习(二十三)选择结构-switch语句

    2024-02-02 12:56:01       58 阅读
  6. React状态管理Zustand简单介绍和使用

    2024-02-02 12:56:01       51 阅读
  7. 如何在redis中存储ndarray

    2024-02-02 12:56:01       49 阅读
  8. 【C语言】深入理解NULL指针

    2024-02-02 12:56:01       48 阅读
  9. github开源代码流程-初始化配置 quick start(2)

    2024-02-02 12:56:01       51 阅读