需求:前台传递过来一个list,把list中的数据保存到数据库或者同步给远程接口,通过多线程,提高效率
一、先自定义定义线程池,不要用JDK提供的线程池
各种线程池的特点如下:
(1)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
(3)newCachedThreadPool将创建一个可缓存的线程池,当请求增加时,可以添加新的线程,线程池的规模不存在任何限制,线程数的理论值最大可以到Integer.MAX_VALUE
(4)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
在实际开发中,我们是怎么使用的?(重点)
实际开发中,线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。
如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题
实际开发中,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式
FixedThreadPool 和 SingleThreadPool,允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
CachedThreadPool 和 ScheduledThreadPool,允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
所以,综上所述,我们都会采用底层的方式来创建线程池,大家自己查阅各种线程池的源代码就可以看到他们都是采用了同一个类来创建。
//起的线程数
private static final Integer THREAD_NUM = 5;
ThreadPoolExecutor pool = new ThreadPoolExecutor(THREAD_NUM, THREAD_NUM * 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
二、创建任务类,用来处理任务
需要把所有任务(list)传递进来,并且根据情况切分该list,这样每个线程用来完成一部分任务,比如一共20个数据,共有5个线程,每个线程处理4个数据
public class HandleCallable implements Callable<Integer> {
private List<String> data;//总的任务
private int start;
private int end;
public HandleCallable(List<String> data, int start, int end) {
this.data = data;
this.start = start;
this.end = end;
}
@Override
public Integer call() throws Exception {
List<String> subList = data.subList(start, end);
for (int i = 0; i < subList.size(); i++) {
System.out.println(Thread.currentThread().getName()+"处理:"+subList.get(i));
}
System.out.println(Thread.currentThread().getName()+"处理了"+subList.size()+"条!");
return 1;//可以根据实际运行结果返回,演示程序设置为1
}
}
三、创建方法,调用线程池进行处理
@GetMapping("/deal")
public void deal() throws ExecutionException, InterruptedException {
//生产20个任务 模拟前台传递过来的list
List<String> list = new ArrayList();
for (int i = 0; i < 20; i++) {
list.add("任务:" + i);
}
//调用线程池处理方法
handleListCall(list, 5);
}
public void handleListCall(List<String> data, int threadNum) throws ExecutionException,
InterruptedException {
List<Integer> futureList = new ArrayList<>();
int length = data.size();
int tl = length % threadNum == 0 ? length / threadNum : (length
/ threadNum + 1);
for (int i = 0; i < threadNum; i++) {
int end = (i + 1) * tl;
HandleCallable callable = new HandleCallable(data, i * tl, end > length ?
length : end);
FutureTask<Integer> futureTask = new FutureTask(callable);
pool.submit(futureTask);
futureList.add(futureTask.get());//把返回值放到list中,可以方便根据实际结果进行后续 处理逻辑
}
}
处理结果:
pool-2-thread-1处理:任务:0
pool-2-thread-1处理:任务:1
pool-2-thread-1处理:任务:2
pool-2-thread-1处理:任务:3
pool-2-thread-1处理了4条!
pool-2-thread-2处理:任务:4
pool-2-thread-2处理:任务:5
pool-2-thread-2处理:任务:6
pool-2-thread-2处理:任务:7
pool-2-thread-2处理了4条!
pool-2-thread-3处理:任务:8
pool-2-thread-3处理:任务:9
pool-2-thread-3处理:任务:10
pool-2-thread-3处理:任务:11
pool-2-thread-3处理了4条!
pool-2-thread-4处理:任务:12
pool-2-thread-4处理:任务:13
pool-2-thread-4处理:任务:14
pool-2-thread-4处理:任务:15
pool-2-thread-4处理了4条!
pool-2-thread-5处理:任务:16
pool-2-thread-5处理:任务:17
pool-2-thread-5处理:任务:18
pool-2-thread-5处理:任务:19
pool-2-thread-5处理了4条!
可以再优化下,给线程起个跟业务相关的名字,方便排查问题