使用多线程(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的配置与维护,另方面减少频繁查数据库,减少数据库压力,此种方式显得更轻量

posted on 2019-10-15 15:18  牛鼻子老赵  阅读(6624)  评论(0编辑  收藏  举报