精益求精,永无止境

一、使用开发工具

IDEA

二、使用插件

1、Alibaba-Java-Coding-Gidelines 阿里巴巴Java开发规约

2、SonarLint 代码质量检测插件

3、MetricsReload检测圈复杂度

三、注意事项

1、避免IDEA自动导入"*"的问题

编译器操作:

    左上角 File->settings->Editor->Code Style->java->imports

    设置  Class count to use import with '*'  值为500

    设置 Names count to use static import with '*' 值为300

2、删除未使用包的导入。

3、类及方法写注释,标明编写人。

4、类名、方法名、变量做到见名知意。

5、注意代码格式。

6、方法参数个数不宜太多。

7、抽取重复代码。

8、对于IO资源,能够继承处理Closeable的,尽量使用try-with-resource,这样可以去除finally。

9、时间格式转换使用线程安全的:DateFormatUtils.format、DateUtils.parseDate。

10、线程尽量使用线程池管理。

11、注意事务中最好不要使用锁,防止死锁的出现。

12、统一系统日志格式规范,及减少不必要的日志内容,为了容易排查问题,个人觉得最好按业务模块分日志。

13、避免魔法值的出现。

14、避免重复制轮子。

15、利用MetricsReload检测圈复杂度,减少逻辑的判断。

16、注意数据库中触发器的使用是否会影响到插入数据的返回结果。

 

四、个例问题处理

1、定时任务动态读取数据库配置

@Scheduled等的配置可以读取配置文件值,及初始化的常理,但不能动态读取配置,因此需要做优化

解决方案:重写 SchedulingConfigurer 的 configureTasks 方法,添加自定义的触发方法

具体代码如下:

 

@Slf4j
@Configuration
public class SchedulingExecutorConfig implements SchedulingConfigurer, AsyncConfigurer {

    private ScheduledTaskRegistrar taskRegistrar;
    private Map<String, TriggerTask> triggerTaskMap = new ConcurrentHashMap<>();
    private Map<String, TriggerTask> notDealTriggerTaskMap = new ConcurrentHashMap<>();

    /**
     * 定时任务使用的线程池
     * @return
     */
    @Bean(destroyMethod = "shutdown", name = "taskScheduler")
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        scheduler.setThreadNamePrefix("scheduler-");
        scheduler.setAwaitTerminationSeconds(120);
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        return scheduler;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        this.taskRegistrar = scheduledTaskRegistrar;
        scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
        if(!notDealTriggerTaskMap.isEmpty()){
            for (Map.Entry<String, TriggerTask> entry : notDealTriggerTaskMap.entrySet()){
                    addTriggerTask(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 添加任务
     * @param taskId 任务id
     * @param triggerTask
     */
    public void addTriggerTask(String taskId, TriggerTask triggerTask) {
        if(StringUtils.isEmpty(taskRegistrar)){
            notDealTriggerTaskMap.put(taskId, triggerTask);
            return;
        }
        if(triggerTaskMap.keySet().contains(taskId)){
            return;
        }
        taskRegistrar.addTriggerTask(triggerTask.getRunnable(), triggerTask.getTrigger());
        triggerTaskMap.put(taskId, triggerTask);
    }

    public ScheduledTaskRegistrar getTaskRegistrar() {
        return this.taskRegistrar;
    }
}

 

定时任务的类可继承InitializingBean

 

@Override
public void afterPropertiesSet() {
    try {
        schedulingExecutorConfig.addTriggerTask("paramRecvSchedulerCheckVersion",new TriggerTask(this :: checkVersion
        , triggerContext -> {
            paramConfig.initCheckVersionFixedDelay();
            // 任务触发,可修改任务的执行周期
            PeriodicTrigger trigger = new PeriodicTrigger(Long.parseLong(paramConfig.getCheckVersionFixedDelay()));
            return trigger.nextExecutionTime(triggerContext);
        }));
    } catch (Exception e) {
        log.error("初始化定时任务异常:", e);
    }
}

 

2、关闭tomcat出现长时间未关闭问题

经使用jconsole发现,关闭tomcat时,线程池并没有关闭

 

解决方案:关闭tomcat时,关闭队列中等待执行的任务线程,正在执行的,等执行完再关闭,如果超两分钟还没有关闭的,强制关闭线程,些处虽说有可能涉及到事务,但影响不大,最多只是回滚事务

 

 

/**
 * 容器关闭监听器
 * @author djz
 */
@Slf4j
@Component
public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {

    @Resource
    @Qualifier("paramTaskExecutor")
    private ThreadPoolTaskExecutor paramTaskExecutor;

    @Resource
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    @Qualifier("taskScheduler")
    private ThreadPoolTaskScheduler taskScheduler;

    @Resource
    private SchedulingExecutorConfig schedulingExecutorConfig;

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        log.info("onApplicationEvent start");
        //关闭线程池
        paramTaskExecutor.shutdown();
        log.info("paramTaskExecutor线程池已关闭");

        taskExecutor.shutdown();
        log.info("taskExecutor线程池已关闭");
        ScheduledTaskRegistrar taskRegistrar = schedulingExecutorConfig.getTaskRegistrar();
        shutDownScheduledTask(taskRegistrar.getScheduledTasks());

        try {
            taskScheduler.shutdown();
        } catch (Exception e) {
            log.info("执行taskScheduler线程池关闭异常", e);
        }
        log.info("taskScheduler线程池已关闭");
    }

    /**
     * 关闭队列的定时任务
     * @param scheduledTasks
     */
    private void shutDownScheduledTask(Set<ScheduledTask> scheduledTasks) {
        log.info("关闭队列的定时任务:{}", JSON.toJSON(scheduledTasks));
        Iterator<ScheduledTask> iterator = scheduledTasks.iterator();
        while (iterator.hasNext()) {
            ScheduledTask scheduledTask = iterator.next();
            Class<?> scheduledTaskClass = scheduledTask.getClass();
            try {
                Field field = scheduledTaskClass.getDeclaredField("future");
                field.setAccessible(true);
                ScheduledFuture<?> future = (ScheduledFuture<?>) field.get(scheduledTask);
                if (future != null && !future.isDone()) {
                    future.cancel(false);
                }
            } catch (Exception e) {
                log.info("异常", e);
            }
        }
    }
}

 

也注意线程池的配置

//表明等待所有线程执行完,默认为false

setWaitForTasksToCompleteOnShutdown(true);

 

//设置等待时间后,强制关闭线程

setAwaitTerminationSeconds(120);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值