四种线程池
newCachedThreadPool
创建一个可根据需要创建新线程的线程池,但是在以前可用的线程构造时重用它们。对于执行很多短期异步任务的程序而言,这种线程池可以极大的提高程序性能。调用execute将重用以前构造的线程,如果现有线程没有可用的,则创建新线程执行,并且把池子中那些超过60s未使用的线程。因此长时间保持空闲的线程池也不会占用很多的资源。
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
try{
System.out.println(Thread.currentThread().getName() + "执行前before...");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "执行后after...");
}catch(Exception e){
}
});
}
executorService.shutdown();
}
运行结果:
pool-1-thread-1执行前before… pool-1-thread-4执行前before… pool-1-thread-3执行前before… pool-1-thread-2执行前before… pool-1-thread-5执行前before…
pool-1-thread-5执行后after…
pool-1-thread-3执行后after…
pool-1-thread-2执行后after…
pool-1-thread-1执行后after…
pool-1-thread-4执行后after…
newSingleThreadExecutor
创建一个单一线程的线程池,简而言之就是这个线程池只有一个线程,线程池可以在这个线程死后重新启动一个线程替代原来的线程继续执行下去
public static void main(String[] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
try{
System.out.println(Thread.currentThread().getName() + "执行前before...");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "执行后after...");
}catch(Exception e){
}
});
}
executorService.shutdown();
}
运行结果:
pool-1-thread-1执行前before…
pool-1-thread-1执行后after…
pool-1-thread-1执行前before…
pool-1-thread-1执行后after…
pool-1-thread-1执行前before…
pool-1-thread-1执行后after…
pool-1-thread-1执行前before…
pool-1-thread-1执行后after…
pool-1-thread-1执行前before…
pool-1-thread-1执行后after…
newFixedThreadPool
创建一个可重用固定大小的线程池,用共享的无界队列来运行这些线程。在任意点,在大多数nThreads线程会处于处理任务的活跃状态。如果在所有线程都处于活跃状态处理任务时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,则启动一个新的线程来代替它执行任务。在某个线程被显示的关闭期间,线程会一直在池子中。
public static void main(String[] args){
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
executorService.execute(() -> {
try{
System.out.println(Thread.currentThread().getName() + "执行前before...");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "执行后after...");
}catch(Exception e){
}
});
}
executorService.shutdown();
}
newScheduledThreadPool
创建一个线程池,它可安排在给定延迟后执行任务或者定期的执行。
public static void main(String[] args){
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟3秒,执行1次");
}
},3,TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("延迟1秒,2秒执行一次");
}
},1, 2,TimeUnit.SECONDS);
}
ThreadPoolExecutor
阿里巴巴编码规范中命令禁止使用上述四钟线程池(定时的线程池除外),应该使用ThreadPoolExecutor来创建线程池。
而且创建线程池时,要自定义线程工厂,这样可以自定义有意义的线程名,方便出现问题时的回溯。
自定义的线程工厂MyThreadFactory.java
public class MyThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
public MyThreadFactory(String groupName) {
namePrefix = "MyThreadFactory-" + groupName + "-Worker-";
}
@Override
public Thread newThread(Runnable r) {
String name = namePrefix + nextId.getAndIncrement();
Thread thread = new Thread(null,r,name,0);
return thread;
}
}
线程池的使用:
public static void main(String[] args){
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new MyThreadFactory("test"));
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "正在执行任务");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
ThreadPoolExecutor参数:
- corePoolSize:核心线程数的大小
- maximumPoolSize:最大线程数的大小
- keepAliveTime:当前线程大于核心线程数时,空闲线程在终止之前等待工作的活跃时间
- unit:空闲线程活跃时间单位
- workQueue:工作队列。当线程数超过核心线程数时,就进入工作队列等待。
- threadFactory:线程工厂,可以自定义线程工厂,配置线程的名称。
- handler:拒绝策略。当线程超过最大线程数时执行的拒绝策略。
工作原理(先入队列,后起线程)
当第一个请求来的时候,会给它分配一个线程去执行;当请求数大于核心线程数小于最大线程数的时候,请求进入到阻塞队列中等待,当有资源释放立即从队首取出任务分配给该线程;当队列满的时候,并且请求数小于最大线程数,会重新启动新的线程执行任务;当请求数大于最大线程数的时候,会执行拒绝策略。