当前位置:   article > 正文

【Java|多线程与高并发】线程池详解_java高并发和多线程项目

java高并发和多线程项目


在这里插入图片描述

1. 线程池简介

Java线程池是一种用于管理和重用线程的机制,它可以在需要执行任务时,从线程池中获取线程,执行任务,然后将线程放回池中,以便后续使用。线程池可以有效地管理线程的数量,提高程序的性能和资源利用率。

为什么从线程池里面取线程比直接创建线程快呢?

创建线程是要在操作系统内核中完成的,涉及"用户态"到"内核态"切换操作. 这个切换是有一定开销的. 而线程池取线程是纯的用户态实现.

2. 创建线程池

注意这里线程池创建的写法:

ExecutorService executorService = Executors.newCachedThreadPool();
  • 1

这里并没有new,而是通过Executors的静态方法newCachedThreadPool来创建的,这里涉及到一种设计模式-“工厂模式

3. 工厂模式简介

工厂模式(Factory Pattern)是一种创建对象的设计模式,它提供了一种封装对象创建过程的方式,使得客户端代码与具体对象的实例化过程解耦。

工厂模式的主要目的是通过一个工厂类来创建对象,而不是直接在客户端代码中使用new关键字来实例化对象。这样可以隐藏具体对象的创建细节,提供更灵活、可扩展的设计。

详细可参考:工厂模式 | 菜鸟教程

4. 线程池的使用

线程池的使用很简单,只需要实现submit方法即可

public class Demo19 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行的任务1");
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行的任务2");
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行的任务3");
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里主要是将任务放到线程池中,线程池里的线程就会去执行这些任务

运行结果:

在这里插入图片描述

5. 实现线程池

要想实现线程池,要先弄清楚线程池有哪些功能.

一个线程池中可以同时提交N个任务,线程池里有M个线程来执行这N个任务

如何将N个任务分配给M个线程呢?

可以使用生产者-消费者模型解决这个问题.

以下是一个线程池的简易实现:

public class MyThreadPool {
    private BlockingQueue<Runnable> blockingQueue = new public class MyThreadPool {
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        // 将任务添加到阻塞队列中
        blockingQueue.put(runnable);
    }

    public MyThreadPool(int m){
        // 创建m个线程
        for (int i = 0; i < m; i++) {
            Thread t = new Thread(() ->{
                // 让它一直扫描阻塞队列
                while (true) {
                    try {
                        // 取出任务,并执行
                        Runnable runnable = blockingQueue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

测试上述代码:

public class Demo20 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(5);
        for (int i = 0; i < 10; i++) {
            int temp = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务"+ temp);
                }
            });
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

运行结果:

在这里插入图片描述

不过上述代码中创建的线程数量是"固定"的,可根据需要自行调整

6. ThreadPoolExecutor的构造方法讲解

ThreadPoolExecutor是Java中线程池的一个具体实现类,它实现了ExecutorService接口。ExecutorService是一个高级的线程池管理接口,提供了更多的灵活性和功能。

ThreadPoolExecutor这个类的构造方法比较复杂
在这里插入图片描述

参数:

  • corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut

  • maximumPoolSize - 池中允许的最大线程数

  • keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。

  • unit - keepAliveTime参数的时间单位

  • workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。(自己传入的任务队列,如果传了就用传入的队列.如果不传,就线程池就去创建)

  • threadFactory - 执行程序创建新线程时使用的工厂

  • handler -任务执行被拒绝时使用的处理程序,因为达到线程限制和队列容量

任务执行被拒绝时使用的程序有以下四个:

在这里插入图片描述

实际开发中,主要根据需求选择这里的拒绝策略进行处理.

7. 线程池的线程数量,如何确定?

线程池可以自定义线程的数量

那么在实际开发中,线程池的线程数量设置成多少比较合适?

这个问题主要取决于以下几点:

  1. 任务类型:不同类型的任务对线程数量的需求不同。如果是CPU密集型任务,通常线程数量应该与CPU核心数相近,以充分利用CPU资源。如果是IO密集型任务,可以设置较多的线程数量,以便处理并发的IO操作。
  2. 系统资源:需要考虑系统的硬件资源和性能。如果系统资源有限,例如内存、CPU等,线程数量应适当控制,避免过多的线程导致资源竞争和性能下降。
  3. 并发量:需要根据任务的并发量来确定线程数量。如果任务并发量较高,可以增加线程数量以提高任务处理能力。如果任务并发量较低,可以减少线程数量以节省资源。
  4. 响应时间:需要考虑任务的响应时间要求。如果任务需要快速响应,可以增加线程数量以减少等待时间。如果任务的响应时间可以接受较长的延迟,可以减少线程数量以节省资源。
  5. 线程池类型:不同类型的线程池有不同的线程数量限制。例如,FixedThreadPool固定线程池的线程数量是固定的,CachedThreadPool可缓存线程池的线程数量是动态调整的。

这个问题要考虑的因素有很多,最简单的方法就是去测试

将线程分为设置成不同的数量,然后观察系统的性能和任务的执行情况,根据需要逐步调整线程数量,找到最优的配置。

在这里插入图片描述

感谢你的观看!希望这篇文章能帮到你!
专栏: 《从零开始的Java学习之旅》在不断更新中,欢迎订阅!
“愿与君共勉,携手共进!”
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/498841
推荐阅读
相关标签
  

闽ICP备14008679号