Java并发-线程池

时间:2022-08-20 18:00:24

线程池

1. 对比

new Thead 弊端

  • 每次 new Thread 新建对象,性能差
  • 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或OOM
  • 缺乏更多功能,如更多执行,定期执行,线程中断

线程池的好处

  • 重用存在的线程,减少对象创建,消亡的开销,性能佳
  • 可有效控制最大并发线程数,提供系统资源利用率,同时可以避免过多资源竞争,避免阻塞。
  • 可以提供定时执行,定期执行,单线程,并发数控制等功能

1.2 ThreadPoolExecutor

  • corePoolSize:核心线程数量
  • maximumPoolSize:线程最大线程数
  • workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响

如果线程池中的线程数小于corePoolSize,直接创建新线程来处理任务,即使线程池的其他线程时空闲的,如果线程池中的线程数大于等于 corePoolSize 小于maximumPoolSize,则只有当workQueue满的时候才会去创建新的线程执行任务。

如果我们设置的corePoolSize和maximumPoolSize相同的话,则创建的线程池大小是固定的,如果有新的任务提交,如果workQueue没慢的话,则入队,等到线程池中有空的线程再执行。

  • keepAliveTime:线程没有任务执行时最多保持多久时间终止
  • unit:keepAliveTime的时间单位
  • threadFactory:线程工厂,用来创建线程
  • rejectHandler:当拒绝处理任务时的策略
    • 抛出异常
    • 抛弃前面的任务,执行新的
    • 直接抛弃
    • 用调用者线程执行

1.3 线程池状态

Java并发-线程池

  • Running:能接受新提交的任务,也能处理阻塞队列中的任务
  • Shutdown:关闭状态,不能接受新提交的任务,可以处理阻塞队列中保存的任务。
  • Stop:不接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。
  • Tidying:如果所有任务都终止了。
  • Terminated:调用了terminated()方法。

ThreadPoolExecutor提供的基础方法

  • execute():提交任务,交给线程池执行。
  • submit():提交任务,能够返回执行结果 execute+Future
  • shutdown():关闭线程池,等待任务都执行完
  • shutdownNow():关闭线程池,不等待任务执行完

常用于监控的方法

  • getTaskCount():线程池已执行和未执行的任务总数
  • getCompletedTaskCount():已完成的任务数量
  • getPoolSize():线程池当前的线程数量
  • getActiveCount():当前线程池中正在执行任务的线程数量

1.4 Executor框架接口

  • Executors.newCachedThreadPool:创建可缓存的线程池,如果线程池长度超过处理需要,可以回收,如果么有回收的,则新建线程
  • Executors.newFixedThreadPool:定长的线程池,可以控制并发的线程数,超出的线程再队列中等待
  • Executors.newScheduledThreadPool:创建定长的线程池,支持定时以及周期性的任务执行
  • Executors.newSingleThreadExecutor:创建的是一个单线程化的线程池,只用唯一一个工作线程执行任务,指定按照某种顺序执行。
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/** * Created By liuyao on 2018/4/23 17:21. */
@Slf4j
public class ThreadPoolExample4 {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService=Executors.newScheduledThreadPool(5);
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                log.info("schedual run");
            }
        },3, TimeUnit.SECONDS);

        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                log.info("schedual run");
            }
        },1,3,TimeUnit.SECONDS); //在线程池创建后1秒每隔3秒去执行任务

// scheduledExecutorService.shutdown();

        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.info("timer run");
            }
        },new Date(),3*1000); //定时3秒继续执行任务
    }
}

上面利用了newScheduledThreadPool创建定时执行的任务和利用Timer也可以定时执行任务。

2. 线程池的配置

  • CPU密集型任务,就需要尽量压榨CPU,参考值可以设为NCPU+1
  • IO密集型任务,参考值可以设为2*NCPU