Java并发:线程池的创建和常用方法

由于设计与操作系统的交互,构建一个新的线程开销一般较大。当任务数量较多时,可以创建一个包含一定数量线程的线程缓存池,如果其中某个线程完成了一个任务的执行,可以给其再行分配一个新的任务让其接着执行,从而避免了创建过多的线程。当线程的任务运行完毕后,这个线程不会终止,而是留在池中准备为下一个请求提供服务。

线程池的创建

ExecutorService为表示线程池的接口,可以使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象。

//ThreadPoolExecutor的构造方法
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
参数说明
corePoolSize核心线程数量
maximumPoolSize最大线程数量,包括核心线程和临时线程
keepAliveTime临时线程存活时间
unit临时线程存活时间单位
workQueue任务队列
threadFactory(可选)线程创建方式
handler(可选)任务拒绝策略

此外,创建线程池还有一种方法,可以通过Executors线程池工具类调用方法返回不同特点的线程池对象,但“大厂”一般强制不允许使用Executors创建线程池,而是通过ThreadPoolExecutor的方式。

在大型并发系统环境中使用Executors如果不注意可能会出现系统风险,如Executors返回的线程池对象存在部分参数设置为Integer.MAX_VALUE,可能会堆积大量请求或创建大量线程。


部分参数说明

核心线程和临时线程

核心线程数量:一般来说,对于计算密集型任务的核心线程数量会设置为CPU核数+1,对于IO密集型任务的核心线程数量会设置为CPU核数*2

创建临时线程:当新任务提交时发现核心线程都在忙,任务队列也满了,并且当前线程数量未达到最大线程数量,此时才会创建临时线程。

任务队列

BlockingQueue是表示阻塞队列的一个接口,任务队列是基于阻塞队列实现的,即采用生产者和消费者的模式。

Java中提供了7种BlockingQueue的实现类:

实现类说明
ArrayBlockingQueue数组结构组成的有界阻塞队列
DelayQueue链表结构组成的有界阻塞队列
LinkedBlockingDeque支持优先级排序的无界阻塞队列
LinkedBlockingQueue二叉堆实现的无界优先级阻塞队列
LinkedTransferQueue不存储元素的阻塞队列
PriorityBlockingQueue使用双向队列实现的有界双端阻塞队列
SynchronousQueue高效链表实现的无界阻塞队列

线程创建方式

ThreadFactory是表示线程创建方式的一个接口。

最简单的一个实现类如下:

class SimpleThreadFactory implements ThreadFactory {
    public Thread newThread(Runnable r) {
        return new Thread(r);
    }
}

如果构造方法中没有指定创建方式,系统将会使用默认的创建方式Executors.defaultThreadFactory() :

private static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        @SuppressWarnings("removal")
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" + poolNumber.getAndIncrement() +"-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

任务拒绝策略

拒绝新任务:已经达到最大线程数量,核心线程和临时线程都在忙,任务队列也满了,此时新任务到来时会拒绝。

任务拒绝策略要求实现RejectedExecutionHandler接口。

Java中提供了4种RejectedExecutionHandler的实现类:

实现类说明
ThreadPoolExecutor.AbortPolicy(默认)丢弃任务并抛出RejectedExecutionException 异常
ThreadPoolExecutor.CallerRunsPolicy由调用线程处理该任务
ThreadPoolExecutor.DiscardOldestPolicy丢弃任务,但是不抛出异常,可以配合这种模式进行自定义的处理方式
ThreadPoolExecutor.DiscardPolicy丢弃队列最早的未处理任务,然后重新尝试执行任务

线程池常用方法

方法说明
void execute(Runnable command)执行Runnable任务
Future<T> submit(Callable<T> task)执行Callable任务,返回任务对象用于获取线程返回的结果
void shutdown等全部任务执行完毕后再关闭线程池
List<Runnable> shutdownNow()立即关闭线程池,返回队列中未执行的任务
boolean isTerminated()如果所有任务在关闭后都已完成则返回true
boolean isShutdown()如果线程池已关闭则返回true
//运用线程池实现对同一任务对象进行20个计数1000的线程

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        CountClass count = new CountClass();
        Runnable runnable = new MyRunnable(count);
        ExecutorService threadPool = new ThreadPoolExecutor(17, 20, 1000, 
                TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10), 
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 20; i++) {
            threadPool.execute(runnable);
        }
        threadPool.shutdown();
        while (!threadPool.isTerminated());
        System.out.println(count.getCountNum());

    }
}

class MyRunnable implements Runnable {
    private CountClass count;
    public MyRunnable(CountClass count){
        this.count = count;
    }
    @Override
    public void run() {
        count.countAdd();
    }
}

class CountClass{
    private int countNum=0;
    public void countAdd(){
        for (int i = 0; i < 1000; i++) {
            synchronized (this) {
                countNum++;
            }
        }
    }
    public int getCountNum() {
        return countNum;
    }
}

参考资料

【精选】Java 多线程:彻底搞懂线程池_多线程线程池-CSDN博客

黑马程序员-Java入门基础视频教程-多线程:线程池

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

STRANGEX-03

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

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

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

打赏作者

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

抵扣说明:

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

余额充值