线程池

一、java 原生线程池

1.ThreadPoolExecutor的重要参数

corePoolSize:核心线程数

核心线程会一直存活,即使没有任务需要执行

当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

queueCapacity:任务队列容量(阻塞队列)

当核心线程数达到最大时,新任务会放在队列中排队等待执行

maxPoolSize:最大线程数

当线程数>=corePoolSize,并且<maxPoolSzie,且任务队列已满时。线程池会创建新线程来处理任务

当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

keepAliveTime:线程空闲时间

当线程空闲时间达到keepAliveTime时,线程会被回收,直到线程数量=corePoolSize

如果allowCoreThreadTimeout=true,则会直到线程数量=0

allowCoreThreadTimeout:允许核心线程超时

threadFactory:通过这个参数你可以自定义如何创建线程,例如你可以给线程指定一个有意义的名字。

rejectedExecutionHandler:任务拒绝处理器

通过这个参数你可以自定义任务的拒绝策略。如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。至于拒绝的策略,你可以通过 handler 这个参数来指定。ThreadPoolExecutor 已经提供了以下 4 种策略。

  • CallerRunsPolicy:提交任务的线程自己去执行该任务。
  • AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。
  • DiscardPolicy:直接丢弃任务,没有任何异常抛出。
  • DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。

线程池配置:

核心数一般配置为CPU核数 + 1 ,多个的话,线程切换会导致额外的消耗。

最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

可以使用APM工具

 

2.ThreadPoolExecutor执行顺序:

 线程池按以下行为执行任务

当线程数小于核心线程数时,创建线程。

当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

当线程数大于等于核心线程数,且任务队列已满

若线程数小于最大线程数,创建线程

若线程数等于最大线程数,抛出异常,拒绝任务

3.类型

可以使用JDK的线程池工具来创建

 Executors.newCachedThreadPool();   //可缓存的线程池
 Executors.newFixedThreadPool();    //定长线程池
 Executors.newScheduledThreadPool(); //定时
 Executors.newSingleThreadExecutor() //单个线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future submit = executorService.submit(() -> {
            System.out.println(12323);
        });
        while (true){
            if(submit.isDone()){         //可以用来判断线程是否执行完成
                System.out.println("執行完成");
                break;
            }
        }

一般不建议使用该方法创建线程池,因为默认的使用的都是无界的 LinkedBlockingQueue,高负载情境下,无界队列很容易导致 OOM,而 OOM 会导致所有请求都无法处理,这是致命问题。所以强烈建议使用有界队列

异常处理:ThreadPoolExecutor 对象的 execute() 方法提交任务时,如果任务在执行的过程中出现运行时异常,会导致执行任务的线程终止;不过,最致命的是任务虽然异常了,但是你却获取不到任何通知,这会让你误以为任务都执行得很正常。虽然线程池提供了很多用于异常处理的方法,但是最稳妥和简单的方案还是捕获所有异常并按需处理,你可以参考下面的示例代码。

为不同的任务创建不同的线程池,如果提交的任务有依赖关系,一定不要提交到相同的线程池。

try {
  // 业务逻辑
} catch (RuntimeException x) {
  // 按需处理
} catch (Throwable x) {
  // 按需处理
} 

4 关闭线程池

有俩种方法,有shutdown 和shutdownNow 。 原理是遍历线程中的工作线程,然后逐个调用interrupt方法来中断线程,所以无法响应中断的任务可能永远都不会终止。

shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的则中断。而shutdownNow则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回

5 线程池监控

taskCount: 线程池需要执行的任务数量

completedTaskCount: 线程池运行过程中已完成的任务数量,小鱼或等于taskCount

largerPoolSize: 曾经创建多的最大线程数量,通过该数据可以得知时候曾经满过。

getPoolSize: 线程池的线程个数

getActiveCount:获取活动的线程数

也可以通过重写线程池的一些方法,如 beforeExecute , afterExecute, terminated 方法。 

6 ExecutorService 创建的线程池

1 固定线程池, 核心数与最大线程数一样,且使用无界阻塞队列。 

由于使用的是无界队列,所以空闲时间以及空闲时间单位是无效的 , 且不会触发拒绝策略。 

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

2 单个线程池,核心数与最大线程数均为1,且使用无界阻塞队列。 影响同固定线程池, 将所有任务串行化。 

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

3 缓存线程池

核心数为0 ,最大线程数为Integer的最大值,且使用SynchronousQueue 做为队列。由于SynchronousQueue不存储数据,所有当任务提交时,如果此时没有空余的线程,那么会创建线程,如果完成任务的速度小于提交任务的速度,那么会创建很多的线程,会耗尽CPU和内存资源。  

使用SynchronousQueue的目的就是保证“对于提交的任务,如果有空闲线程,则使用空闲线程来处理;否则新建一个线程来处理任务”。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

4 schedule 线程池

可以看到核心线程数为传入的参数, 最大线程数为Integer最大值,且使用DelayedWorkQueue 做为队列(无界)。

DelayedWorkQueue 是一个优先队列,会按照执行时间来进行排序,小的排在前面。 

当某个线程从队列中拿到头节点的执行任务,会比较他的执行时间是否大于当前时间,如果大于,则等待,到执行时间苏醒,开始执行任务。 

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    //最终调用的是ThreadPoolExecutor 构造方法
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

二 手动实现线程池

利用了消费者,生产者模式,创建若干线程,来进行queue的读取。达到线程池的目的

public class MyThreadPool {

    private BlockingQueue<Runnable> workQueue;

    List<WorkThread> threads;

    public MyThreadPool(int poolSize,BlockingQueue<Runnable> workQueue){
        this.workQueue = workQueue;
        threads = new ArrayList<>(poolSize);
        for (int i=0;i<poolSize;i++){
            WorkThread work = new WorkThread();
            work.start();
            threads.add(work);
        }
    }

    void execute(Runnable runnable){
        try {
            workQueue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private class WorkThread extends Thread{

        @Override
        public void run() {
            while (true){
                try {
                    Runnable task = workQueue.take();
                    task.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    public static void main(String[] args){
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        MyThreadPool pool = new MyThreadPool(10,workQueue);
        pool.execute(()->{
            System.out.println("hello");
        });
    }
}

 

二、spring 线程池实现

1、进行线程池的配置:

@Configuration
@EnableAsync                               //需要添加注解一开启对多线程的支持
@ComponentScan(basePackages = "com.test.asyn")
public class AsynConfig  {

    @Bean
    public Executor taskExecutor() {       //需要配置一个ExecutorBean
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);
        executor.setQueueCapacity(30);
        executor.setMaxPoolSize(50);
        executor.setKeepAliveSeconds(1000);
        executor.initialize();           //这里一定要进行初始化
        return executor;
    }
}

2.实现:

@Service
public class AsynService {

    @Async     //添加到方法表示这个方法是支持异步的,添加到类上表示类中的全部方法都是异步的
    public void exec(int i){
        try {
            Thread.sleep(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行异步任务"  + "=====" + i);
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值