JAVA提供了4种可用的线程池方法:
1,newSingleThreadExecutor():单例线程,连接池中只会存在一个线程
2,newFixedThreadPool(int nThread):固定数量的线程池,当超过现在数量,新的线程必须等待有线程被移除
3,newCacheedThreadPool():缓存线程池,当某个线程在创建时,先查看线程池中是否有存在的线程,没有则创建一个;可以设置线程的最大执行时长,默认为60s
4,newScheduledThreadPool():计划任务线程池,可以对线程设置周期执行
好处:线程池可用让程序更加专注于执行任务本身,而不必为线程的启动和关闭耗费时间
比如,有这么一个需求:在微信公众号中输入“订单”,查询出当天不同渠道的所有订单,这其实是一个很耗时的查询,若单个任务去执行,微信服务器可能等不了多久就直接返回“连接超时”了,此时想到的就是使用连接池分发多个任务去执行查询,最后将统计结果汇总
1,newFixedThreadPool来执行固定任务:
下面新建了一个固定大小的线程池,用来执行StatTask线程
ExecutorService pool = Executors.newFixedThreadPool(6); Future<String> task1 = pool.submit(new StatTask(ORDER_TASK1, date)); Future<String> task2 = pool.submit(new StatTask(ORDER_TASK2, date)); Future<String> task3 = pool.submit(new StatTask(ORDER_TASK3, date)); Future<String> task4 = pool.submit(new StatTask(ORDER_TASK4, date)); Future<String> task5 = pool.submit(new StatTask(ORDER_TASK5, date)); Future<String> task6 = pool.submit(new StatTask(ORDER_TASK6, date)); pool.shutdown(); try { resultMap.put(ORDER_TASK1, task2.get(3, TimeUnit.SECONDS)); } catch (Exception e) { logger.error("error", e); resultMap.put(ORDER_TASK1, "0"); } ....
这里调用了pool.submit方法,表示有返回值的,submit里面的参数必须实现callable接口,返回Future对象,表中任务执行结果对象;若调用pool.execute方法则没有返回值,execute里面的参数需要实现Runnable接口
使用shutdown()方法,不再接受新的任务,以前的任务可以继续执行
Future.task方法为堵塞执行,参数可设置最大的执行时长,到时间则自动终止
class StatTask implements Callable<String> { private String taskName; private String date; StatTask(String taskName, String date) { this.taskName = taskName; this.date = date; } @Override public String call() throws Exception { long starttime = System.currentTimeMillis(); if (ORDER_TASK1.equals(taskName)) { //处理订单1 } else if (ORDER_TASK2.equals(taskName)) { //处理订单2 } ... return "0"; } }
StatTask实现Callable接口,并重写了call方法,在里面处理响应的查询逻辑,Callable使用了泛型,在call方法中返回自定义的类型
以上就新建了6个线程用来处理统计,最后将结果放入resultMap中,每个线程的最大等待时长为3s。
2,newScheduledThreadPool执行定时任务
ScheduledExecutorService pool = Executors.newScheduledThreadPool(20); DataImportTask task = null; for (int i=0;i<20;i++) { int start = i*300; int end = start+300; task = new DataImportTask("dataImport"+i,start,end); pool.scheduleWithFixedDelay(task, 0, 30, TimeUnit.MINUTES); //pool.schedule(task, 30, TimeUnit.MINUTES) }
上面新建了20个定时任务用来导入数据
pool.scheduleWithFixedDelay()方法,第一个参数为任务类,需要继承Runnable接口,第二个参数为第一次执行的延时(纳秒/微妙/毫秒/秒/分/时),第二个参数对于第一次任务执行完成后在推迟多少时间执行,最后一个参数为时间的单位(纳秒/微妙/毫秒/秒/分/时)
pool.schedule表示只周期执行一次,第二个参数就表示第二次推迟的时间