一、线程池的优势

1.降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。

2.提高系统相应速度,当有任务到达时,通过复用已存在的行程,无需等待新线程的创建便能立刻执行。

3.方便线程并发数的管控,因为线程若是无限制创建,可能会导致内存占用过多而产生内存溢出,并且会造成cpu过度切换。

4.提供更强大的功能,延时定时线程池。

二、线程池的种类

Executors类提供了4种不同的线程池:newCachedThreadPool, newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor。

1.newCachedThreadPool:用来创建一个可以无限扩大到线程池,适用于负载较轻的场景,执行短期异步任务。

2.newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。

3.newScheduledThreadPool:适用于执行延时或周期性的任务。

4.newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。

三、线程池的测试

先创建一个自定义的线程类

示例代码如下:

import java.util.Date;
 
/**
 * @author qinxun
 * @date 2023-06-13
 * @Descripion: 自定义线程类
 */
public class MyRunnable implements Runnable {
 
    private String name;
 
    public MyRunnable(String name) {
        this.name = name;
    }
 
    @Override
    public void run() {
        System.out.println(name + "->start time:" + new Date());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name + "->end time:" + new Date());
    }
}

1.使用newCachedThreadPool创建可缓存的线程池

示例代码如下:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author qinxun
 * @date 2023-06-13
 * @Descripion: 线程池测试
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        // 可缓存的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            MyRunnable runnable = new MyRunnable(String.valueOf(i));
            executorService.execute(runnable);
        }
        executorService.shutdown();
        System.out.println("Main Thread:Finished at:" + new Date());
    }
}

程序执行结果:

0->start time:Tue Jun 13 15:18:31 CST 2023
4->start time:Tue Jun 13 15:18:31 CST 2023
2->start time:Tue Jun 13 15:18:31 CST 2023
3->start time:Tue Jun 13 15:18:31 CST 2023
1->start time:Tue Jun 13 15:18:31 CST 2023
Main Thread:Finished at:Tue Jun 13 15:18:31 CST 2023
4->end time:Tue Jun 13 15:18:31 CST 2023
3->end time:Tue Jun 13 15:18:31 CST 2023
1->end time:Tue Jun 13 15:18:31 CST 2023
0->end time:Tue Jun 13 15:18:31 CST 2023
2->end time:Tue Jun 13 15:18:31 CST 2023

根据需要创建自定义数量的线程。

2.使用newFixedThreadPool创建固定长度的线程池。

示例代码如下:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author qinxun
 * @date 2023-06-13
 * @Descripion: 线程池测试
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        // 创建固定长度的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            MyRunnable runnable = new MyRunnable(String.valueOf(i));
            executorService.execute(runnable);
        }
        executorService.shutdown();
        System.out.println("Main Thread:Finished at:" + new Date());
    }
}

程序执行结果:

Main Thread:Finished at:Tue Jun 13 15:23:03 CST 2023
0->start time:Tue Jun 13 15:23:03 CST 2023
2->start time:Tue Jun 13 15:23:03 CST 2023
1->start time:Tue Jun 13 15:23:03 CST 2023
0->end time:Tue Jun 13 15:23:03 CST 2023
1->end time:Tue Jun 13 15:23:03 CST 2023
2->end time:Tue Jun 13 15:23:03 CST 2023
4->start time:Tue Jun 13 15:23:03 CST 2023
3->start time:Tue Jun 13 15:23:03 CST 2023
4->end time:Tue Jun 13 15:23:03 CST 2023
3->end time:Tue Jun 13 15:23:03 CST 2023

我们发现前面固定的3个线程先执行,后续创建新的线程来执行任务。

3.使用newSingleThreadExecutor创建单线程线程池

示例代码如下:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * @author qinxun
 * @date 2023-06-13
 * @Descripion: 线程池测试
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        // 创建单线程线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            MyRunnable runnable = new MyRunnable(String.valueOf(i));
            executorService.execute(runnable);
        }
        executorService.shutdown();
        System.out.println("Main Thread:Finished at:" + new Date());
    }
}

程序执行结果:

Main Thread:Finished at:Tue Jun 13 15:27:15 CST 2023
0->start time:Tue Jun 13 15:27:15 CST 2023
0->end time:Tue Jun 13 15:27:15 CST 2023
1->start time:Tue Jun 13 15:27:15 CST 2023
1->end time:Tue Jun 13 15:27:15 CST 2023
2->start time:Tue Jun 13 15:27:15 CST 2023
2->end time:Tue Jun 13 15:27:15 CST 2023
3->start time:Tue Jun 13 15:27:15 CST 2023
3->end time:Tue Jun 13 15:27:15 CST 2023
4->start time:Tue Jun 13 15:27:15 CST 2023
4->end time:Tue Jun 13 15:27:15 CST 2023

我们发现当前的线程是一个个顺序执行的。

4.使用newScheduledThreadPool实现延迟或定时的线程池

scheduleAtFixedRate:按指定频率周期执行某个任务,是以上一个任务开始的时间计时,period时间过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。

scheduleWithFixedDelay:周期定时执行某个任务/按指定频率间隔执行某个任务(注意)是以上一个任务结束时开始计时,period时间过去后,立即执行。

示例代码如下:

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 
/**
 * @author qinxun
 * @date 2023-06-13
 * @Descripion: 线程池测试
 */
public class ExecutorsDemo {
    public static void main(String[] args) {
        // 创建单线程线程池
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 5; i++) {
            MyRunnable runnable = new MyRunnable(String.valueOf(i));
            // 延迟1秒,每隔3秒执行一遍(上一个任务结束开始计时)
            executorService.scheduleAtFixedRate(runnable, 1, 3, TimeUnit.SECONDS);
        }
        //executorService.shutdown();
        System.out.println("Main Thread:Finished at:" + new Date());
    }
}

程序执行结果:

Main Thread:Finished at:Tue Jun 13 15:44:10 CST 2023
0->start time:Tue Jun 13 15:44:11 CST 2023
2->start time:Tue Jun 13 15:44:11 CST 2023
1->start time:Tue Jun 13 15:44:11 CST 2023
0->end time:Tue Jun 13 15:44:11 CST 2023
2->end time:Tue Jun 13 15:44:11 CST 2023
3->start time:Tue Jun 13 15:44:11 CST 2023
1->end time:Tue Jun 13 15:44:11 CST 2023
4->start time:Tue Jun 13 15:44:11 CST 2023
4->end time:Tue Jun 13 15:44:12 CST 2023
3->end time:Tue Jun 13 15:44:12 CST 2023
0->start time:Tue Jun 13 15:44:14 CST 2023
2->start time:Tue Jun 13 15:44:14 CST 2023
1->start time:Tue Jun 13 15:44:14 CST 2023
1->end time:Tue Jun 13 15:44:14 CST 2023
0->end time:Tue Jun 13 15:44:14 CST 2023
2->end time:Tue Jun 13 15:44:14 CST 2023
4->start time:Tue Jun 13 15:44:14 CST 2023
3->start time:Tue Jun 13 15:44:14 CST 2023