使用多线程(newSingleThreadScheduledExecutor)创建一个定时任务
业务场景:
1、项目中很多场景下使用到了定时任务,一般采用job的方式
2、一些轻量级的定时操作,如定时查数据库,将数据加载到内存中,不用频繁查数据库,可以采用多线程(newSingleThreadScheduledExecutor)的方式实现显得更轻量高效
废话不多说,直接上代码
(1)、创建一个接口
package com.search.vst.search.service; /** * @desc 商圈 * @author zhanhao */ import com.search.vst.search.beans.vo.PoiBusinessAreaVo; public interface PoiBusinessAreaService { PoiBusinessAreaVo getPoiBusinessArea(String cityId, String keyWords); void updatePoiBusinessAreaConfig(); }
(2)、创建一个实现类
package com.search.vst.search.service.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.PostConstruct; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.search.vst.search.ENV; import com.search.vst.search.beans.vo.PoiBusinessAreaVo; import com.search.vst.search.service.PoiBusinessAreaService; import com.search.vst.search.service.WordTaggingDataService; @Service("poiBusinessAreaService") public class PoiBusinessAreaServiceImpl implements PoiBusinessAreaService { private static Log log=LogFactory.getLog(PoiBusinessAreaServiceImpl.class); @Autowired private WordTaggingDataService wordTaggingDataService; private ReentrantLock trieBuildLock=new ReentrantLock(); private volatile List<PoiBusinessAreaVo> poiBusinessAreaList= new ArrayList<PoiBusinessAreaVo>(); private static ScheduledExecutorService ex=Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("PoiBusinessAreaService_trie_worker_%d").build()); @PostConstruct protected void init() { ex.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { updatePoiBusinessAreaConfig(); } catch (Exception e) { log.warn("updatePoiBusinessAreaConfig init error " + e.getMessage()); } } }, 0, 2, TimeUnit.MINUTES); updatePoiBusinessAreaConfigForFirstTime(); } /** * 因为项目在重启的过程中,如果依赖的search_calculate也在重启,那么调用dubbo会失败,导致tire数构建失败,这里启动一个线程做多次尝试 */ private void updatePoiBusinessAreaConfigForFirstTime() { Thread loopInitThread = new Thread(new Runnable() { @Override public void run() { // 延迟一分钟执行 try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e1) { } log.info(Thread.currentThread().getName() + " start!"); int maxTryTimes = 10; int tryInterval = 20; while (maxTryTimes > 0 && CollectionUtils.isEmpty(poiBusinessAreaList)) { try { maxTryTimes--; updatePoiBusinessAreaConfig(); } catch (Exception e) { if (ENV.isArk()) { log.warn("try to update config error,times:" + maxTryTimes + "@error " + e); } else { log.error("try to update config error,times:" + maxTryTimes, e); } } finally { try { TimeUnit.SECONDS.sleep(tryInterval); } catch (InterruptedException e) { log.error("interupted ,times:" + maxTryTimes, e); } } } log.info(Thread.currentThread().getName() + " finish!"); } }); loopInitThread.setName("poi_buildTrie_loopInitThread"); loopInitThread.start(); } private void buildTrie() { List<PoiBusinessAreaVo> poiBusinessAreas = wordTaggingDataService.getPoiBusinessArea(); this.poiBusinessAreaList = poiBusinessAreas; } @Override public void updatePoiBusinessAreaConfig() { boolean lockRequired = false; try { lockRequired = trieBuildLock.tryLock(10, TimeUnit.SECONDS); if (lockRequired) { buildTrie(); } else { log.warn("lockRequired unsuccessful"); } } catch (InterruptedException e) { log.error("lockRequired fail", e); } finally { if (lockRequired) { trieBuildLock.unlock(); } } } @Override public PoiBusinessAreaVo getPoiBusinessArea(String cityDistrictId, String keyWords) { if (CollectionUtils.isNotEmpty(this.poiBusinessAreaList)) { for (PoiBusinessAreaVo poiBusinessAreaVo : this.poiBusinessAreaList) { if (Objects.equals(cityDistrictId, poiBusinessAreaVo.getCityId()) && Objects.equals(keyWords, poiBusinessAreaVo.getPoiBusinessArea())) { return poiBusinessAreaVo; } } } return null; } }
总结:该业务场景下,一方面减少job的配置与维护,另方面减少频繁查数据库,减少数据库压力,此种方式显得更轻量