标签:alt 存在 比较 auth 完成 author code 测试 影响
我们都知道创建一个线程可以继承Thread类或者实现Runnable接口,实际Thread类就是实现了Runnable接口。
到今天才明白后端线程的作用:我们可以开启线程去执行一些比较耗时的操作,类似于前台的ajax异步操作,比如说用户上传一个大的文件,我们可以获取到文件之后开启一个线程去操作该文件,但是可以提前将结果返回去,如果同步处理有可能太耗时,影响系统可用性。
原生的开启线程执行异步任务的方式:
new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start();
弊端如下:
相比new Thread,Java提供的四种线程池的好处在于:
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
可以用ExecutorService接受返回值,ExecutorService是继承Executor的一个接口,ThreadPoolExecutor继承自AbstractExecutorService,AbstractExecutorService实现ExecutorService接口。一般也用作一个类的静态成员变量,所有实例共用一个ExecutorService对象。
MyThread线程类:继承Thread并且重写run方法,run方法中间隔一秒打印一次线程名字
package cn.qlq.threadTest; /** * 原生的线程类Thread的使用方法 * * @author Administrator * */ public class MyThread extends Thread { /** * 更改线程名字 * * @param threadName */ public MyThread(String threadName) { this.setName(threadName); } @Override public void run() { for(int i=0;i<5;i++){ System.out.println(getName()+"-------"+i); try { Thread.sleep(1*1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
创建方法:
/** * 参数是初始化线程池子的大小 */ private static final ExecutorService batchTaskPool = Executors.newFixedThreadPool(2);
查看源码:(使用了阻塞队列,超过池子容量的线程会在队列中等待)
测试代码:
package cn.qlq.threadTest; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedThreadPoolTest { /** * 参数是初始化线程池子的大小 */ private static final ExecutorService batchTaskPool = Executors.newFixedThreadPool(2); public static void main(String[] args) { for(int i = 0;i < 3;i++){ batchTaskPool.execute(new MyThread("mt"+i)); } } }
结果:
mt1-------0
mt0-------0
mt0-------1
mt1-------1
mt1-------2
mt0-------2
mt1-------3
mt0-------3
mt1-------4
mt0-------4
mt2-------0
mt2-------1
mt2-------2
mt2-------3
mt2-------4
解释:
池子容量大小是2,所以mt0和mt1可以加入到线程池子中,mt2只是暂时的加到等待队列,等mt0或者mt1执行完成之后从队列移除之后mt2就有机会执行。。。。。。。。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
创建方法:
private static final ExecutorService batchTaskPool = Executors.newCachedThreadPool();
查看源码:(使用了同步队列)
测试代码:
package cn.qlq.threadTest; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedThreadPoolTest { private static final ExecutorService batchTaskPool = Executors.newCachedThreadPool(); public static void main(String[] args) { for(int i = 0;i < 3;i++){ batchTaskPool.execute(new MyThread("mt"+i)); } } }
结果:
mt0-------0
mt1-------0
mt2-------0
mt0-------1
mt1-------1
mt2-------1
mt0-------2
mt1-------2
mt2-------2
mt1-------3
mt0-------3
mt2-------3
mt1-------4
mt0-------4
mt2-------4
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。类似于单线程执行的效果一样。
创建方法:
private static final ExecutorService batchTaskPool = Executors.newSingleThreadExecutor();
查看源码;使用的阻塞队列
测试代码:
package cn.qlq.threadTest; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleThreadExecutorTest { private static final ExecutorService batchTaskPool = Executors.newSingleThreadExecutor(); public static void main(String[] args) { for(int i = 0;i < 3;i++){ batchTaskPool.execute(new MyThread("mt"+i)); } } }
结果:
mt0-------0
mt0-------1
mt0-------2
mt0-------3
mt0-------4
mt1-------0
mt1-------1
mt1-------2
mt1-------3
mt1-------4
mt2-------0
mt2-------1
mt2-------2
mt2-------3
mt2-------4
创建一个定长线程池(会指定容量初始化大小),支持定时及周期性任务执行。可以实现一次性的执行延迟任务,也可以实现周期性的执行任务。
创建方法:
private static final ScheduledExecutorService batchTaskPool = Executors.newScheduledThreadPool(2);
查看源码:(使用了延迟队列)
测试代码:
package cn.qlq.threadTest; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledThreadPoolTest { private static final ScheduledExecutorService batchTaskPool = Executors.newScheduledThreadPool(2); public static void main(String[] args) { for (int i = 0; i < 3; i++) { //第一次执行是在3s后执行(延迟任务) batchTaskPool.schedule(new MyThread("my" + i), 3, TimeUnit.SECONDS); //第一个参数是需要执行的任务,第二个参数是第一次的延迟时间,第三个参数是两次执行的时间间隔,第四个参数是时间的单位 batchTaskPool.scheduleAtFixedRate(new MyThread("my" + i), 3,7, TimeUnit.SECONDS); //第一个参数是需要执行的任务,第二个参数是第一次的延迟时间,第三个参数是两次执行的时间间隔,第四个参数是时间的单位 batchTaskPool.scheduleWithFixedDelay(new MyThread("my" + i), 3,5, TimeUnit.SECONDS); } } }
schedule是一次性的任务,可以指定延迟的时间。
scheduleAtFixedRate已固定的频率来执行某项计划(任务)
scheduleWithFixedDelay相对固定的延迟后,执行某项计划 (这个就是第一个任务执行完5s后再次执行,一般用这个方法任务调度)
关于二者的区别:
scheduleAtFixedRate :这个是按照固定的时间来执行,简单来说:到点执行
scheduleWithFixedDelay:这个呢,是等上一个任务结束后,在等固定的时间,然后执行。简单来说:执行完上一个任务后再执行
举例子
scheduledThreadPool.scheduleAtFixedRate(new TaskTest("执行调度任务3"),0, 1, TimeUnit.SECONDS); //这个就是每隔1秒,开启一个新线程
scheduledThreadPool.scheduleWithFixedDelay(new TaskTest("第四个"),0, 3, TimeUnit.SECONDS); //这个就是上一个任务执行完,3秒后开启一个新线程
当然实现任务调度还可以采用quartz框架来实现,更加的灵活。参考:https://www.cnblogs.com/qlqwjy/p/8723358.html
例如我系统中使用的一个ExcutorService的例子:
/** * 同步钉钉组织结构和人员的Action * * @author Administrator * */ @Namespace("/sync") public class SyncGroupAndUserAndBaseInfoAction extends DMSActionSupport { /** * serialID */ private static final long serialVersionUID = 3526083465788431949L; private static final ExecutorService batchTaskPool = Executors.newFixedThreadPool(2); private static Logger logger = LoggerFactory.getLogger(SyncGroupAndUserAndBaseInfoAction.class); @Autowired private GroupAndUserService groupService; @Autowired private BaseInfoService baseInfoService; /** * 同步基本信息的数据 * * @return */ @Action(value = "syncGroupAndUser") public String syncGroupAndUser() { long startTime = System.currentTimeMillis(); logger.info("manual sync groups and users!"); String accessToken = FetchDataUtils.getAccessToken(); if (StringUtils.isBlank(accessToken)) { setPreJs("accessToken is null!"); return "js"; } String groupStr = FetchDataUtils.getGroupStr(accessToken); if (StringUtils.isBlank(groupStr)) { setPreJs("groupStr is null"); return "js"; } Set<String> dingGroupIds = FetchDataUtils.getGroupIds(groupStr);// 钉钉同步回来的组 //新开一个线程去获取钉钉用户和组织 batchDisposeDingGroupAndUser(dingGroupIds,groupStr,accessToken); Map<String,Object> response = new HashMap<String,Object>(); response.put("success", true); response.put("message", "success sync datas!"); setPreJs(APIUtils.getJsonResultFromMap(response)); long endTime = System.currentTimeMillis(); logger.info("同步钉钉组织结构和用户完成-----用时:{}ms",(endTime-startTime)); return "js"; } private void batchDisposeDingGroupAndUser(final Set<String> dingGroupIds, final String groupStr,final String accessToken) { Runnable run = new Runnable() { @Override public void run() { groupService.batchDisposeGroups(groupStr, dingGroupIds); groupService.fetchAndDisposeUsers(accessToken, dingGroupIds); } }; batchTaskPool.execute(run); } }
注意:
batchDisposeDingGroupAndUser()方法的形参必须声明为final,否则编译错误。
Java ExecutorService四种线程池的简单使用
标签:alt 存在 比较 auth 完成 author code 测试 影响
原文地址:https://www.cnblogs.com/qlqwjy/p/9470414.html