java线程池(详解)

线程池介绍

线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,对线程统一管理

线程池就是存放线程的池子,池子里存放了很多可以复用的线程。

创建线程和销毁线程的花销是比较大的(手动new Thread 类),创建和消耗线程的时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程是比较消耗资源的。(我们可以把创建和销毁的线程的过程去掉)。

使用线程池的优势

1、提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
2、减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
3、提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

四种创建线程池的方式

Executors类(并发包)提供了4种创建线程池方法,这些方法最终都是通过配置ThreadPoolExecutor的不同参数,来达到不同的线程管理效果。

newCacheThreadPool

创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程

newFixedThread

创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。

newScheduleThreadPool

可以创建定长的、支持定时任务,周期任务执行。

newSingleExecutor

创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

newCacheThreadPool

创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程。

总结: 线程池的最大核心线程为无限大(核心线程数为0,最大线程数为Integer.MAX_VALUE),当执行第二个任务时第一个任务已经完成,则会复用执行第一个任务的线程;如果第一个线程任务还没有完成则会新建一个线程。

public static void main(String[] args)  {
    // 创建可缓存线程池
    ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

    for (int i = 0; i < 5; i++) {
        //创建任务
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        };
        newCachedThreadPool.execute(runnable);
    }
}

newFixedThreadPool

创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。

总结:创建指定长度的线程池,任务超出当前线程池执行线程数量则会一直等待,直到运行。

public static void main(String[] args)  {
    // 创建定长线程池
    ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);

    for (int i = 0; i < 5; i++) {
        //创建任务
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        };
        // 将任务交给线程池管理
        newFixedThreadPool.execute(runnable);
    }
}

newScheduledThreadPool

创建定长的、支持定时任务,周期任务执行。

总结:以下案例中延迟2秒后开始执行线程池中的所有任务。

public static void main(String[] args)  {
    // 创建支持定时线程池
    ScheduledExecutorService  newScheduledThreadPool = Executors.newScheduledThreadPool(2);

    for (int i = 0; i < 5; i++) {
        //创建任务
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        // 将任务交给线程池管理,延迟2秒后才开始执行线程池中的所有任务
        newScheduledThreadPool.schedule(runnable, 2, TimeUnit.SECONDS);
    }
}

newSingleExecutor

创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static void main(String[] args)  {
    // 创建单线程-线程池,任务依次执行
    ExecutorService   newScheduledThreadPool = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 5; i++) {
        //创建任务
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        };
        // 将任务交给线程池管理
        newScheduledThreadPool.execute(runnable);
    }
}

 推荐多线程用法

推荐通过 new ThreadPoolExecutor() 的写法创建线程池,这样写线程数量更灵活,开发中多数用这个类创建线程。

ThreadPoolExecutor核心参数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

参数

含义

解释

corePoolSize

线程池中的核心线程数

核心线程生命周期无限,即使空闲也不会死亡。

maximumPoolSize

线程池中最大线程数

任务队列满了以后当有新任务进来则会增加一个线程来处理新任务,

(线程总数 < maximumPoolSize)

keepAliveTime

闲置超时时间

当线程数大于核心线程数时,经过keepAliveTime时间将会回收非核心线程

unit

超时时间的单位

(时/分/秒等)

*

workQueue

线程池中的任务队列

存放任务(Runnable)的容器

threadFactory

为线程池提供创建新线程的线程工厂

*

rejectedExecutionHandler

拒绝策略

新增一个任务到线程池,如果线程池任务队列超过最大值之后,并且已经开启到最大线程数时,默认为抛出ERROR异常

线程池的工作原理

多线程四种拒绝策略

这四种拒绝策略,在ThreadPoolExecutor是四个内部类。

AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();
DiscardPolicy discardPolicy = new ThreadPoolExecutor.DiscardPolicy();
DiscardOldestPolicy discardOldestPolicy = new ThreadPoolExecutor.DiscardOldestPolicy();
CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();

1. AbortPolicy

当任务添加到线程池中被拒绝时,直接丢弃任务,并抛出

RejectedExecutionException异常

2. DiscardPolicy

当任务添加到线程池中被拒绝时,丢弃被拒绝的任务,不抛异常

3. DiscardOldestPolicy

当任务添加到线程池中被拒绝时,丢弃任务队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。

 4. CallerRunsPolicy

被拒绝任务的处理程序,直接在execute方法的调用线程中运行被拒绝的任务。

简单说:就是被拒绝的任务,直接在主线程中运行,不再进入线程池。

CallerRunsPolicy这种方式好多同学可能不太理解,这里给大家看个Demo:

public static void main(String[] args) {
    // 创建单线程-线程池,任务依次执行
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2,
            60, TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(2),
            new ThreadPoolExecutor.CallerRunsPolicy());

    for (int i = 0; i < 10; i++) {
        //创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        };
        // 将任务交给线程池管理
        threadPoolExecutor.execute(runnable);
    }
}

控制台打印:

如何合理配置线程池

使用线程池时通常我们可以将执行的任务分为两类:

  • cpu 密集型任务
  • io 密集型任务

cpu 密集型任务,需要线程长时间进行的复杂的运算,这种类型的任务需要少创建线程(以CPU核数+1为准),过多的线程将会频繁引起上文切换,降低任务处理速度。

而 io 密集型任务,由于线程并不是一直在运行,可能大部分时间在等待 IO 读取/写入数据,增加线程数量可以提高并发度,尽可能多处理任务。

最后给大家补充一个知识点,配置线程池最好的方式是可以动态修改线程池配置,

例如调用线程池的threadPoolExecutor.setCorePoolSize();方法,

搭配分布式配置中心可以随着运行场景动态的修改核心线程数等功能。

🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈

🍎原创不易,感觉对自己有用的话就❤️点赞👉收藏💬评论把。

  • 93
    点赞
  • 244
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
Java中,使用线程池可以有效地管理和调度线程,从而降低线程的创建成本,并提高应用程序的性能。一个实际的Java线程池项目可以包括以下几个方面的实践: 1. 创建线程池:使用Executor框架的工厂方法之一来创建一个线程池,例如使用ThreadPoolExecutor或Executors.newFixedThreadPool等方法。 2. 指定线程池参数:根据应用程序的需求,设置线程池的核心线程数、最大线程数、线程空闲时间等参数。这些参数可以根据实际情况进行调整,以满足应用程序的需求。 3. 提交任务:通过调用线程池的execute()方法,将需要执行的任务(Runnable对象)提交给线程池线程池会根据可用的线程资源和任务队列的情况来决定如何调度任务。 4. 任务队列:线程池会维护一个任务队列,用于存储所有提交给线程池的任务。任务队列可以使用阻塞队列来实现,以确保线程池可以处理大量的任务请求。 5. 线程管理:线程池会维护一些基本的线程统计信息,例如已完成的任务数量、线程的空闲时间等。这些信息可以帮助线程池进行更有效的管理和调度。 通过实践Java线程池项目,我们可以提高应用程序的性能,同时降低线程的创建成本。这种方式可以避免频繁地创建和销毁线程,提高线程的复用率,从而提高应用程序的吞吐量和响应性能。引用和引用提供了关于线程池的相关信息。引用则提供了关于线程池的任务队列的介绍。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [线程池实战](https://blog.csdn.net/Kangyucheng/article/details/122770949)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Java线程池详解,内含实战演练~](https://blog.csdn.net/weixin_43805705/article/details/130359051)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祁_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值