• 欢迎光临~

并发学习记录16:任务调度线程池

开发技术 开发技术 2022-10-05 次浏览

在任务调度池功能加入之前,可以使用java.util.Timer来实现定时功能,Timer的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或者异常会影响到后面的任务。

例子

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j(topic = "ch.ThreadPoolTest06")
public class ThreadPoolTest06 {
    public static void main(String[] args) {
//        method01();
//        method02();
        method03();
    }


    public static void method01() {
        Timer timer = new Timer();
        TimerTask task01 = new TimerTask() {
            @Override
            public void run() {
                log.debug("task 01 running");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        TimerTask task02 = new TimerTask() {
            @Override
            public void run() {
                log.debug("task 02 running");
            }
        };
        log.debug("任务进入规划");
        //预期情况就是任务一执行完之后,任务二才会执行
        timer.schedule(task01, 1000);
        timer.schedule(task02, 1000);
    }

    public static void method02() {
        Timer timer = new Timer();
        TimerTask task01 = new TimerTask() {
            @Override
            public void run() {
                log.debug("task 01 running");
                int i = 1 / 0;
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        TimerTask task02 = new TimerTask() {
            @Override
            public void run() {
                log.debug("task 02 running");
            }
        };
        log.debug("任务进入规划");
        //预期情况就是任务一执行出现异常,那么任务二就不会执行
        timer.schedule(task01, 1000);
        timer.schedule(task02, 1000);
    }

    //预期是同时执行,任务调度线程池在线程数目够的情况下会并发执行,而且一个线程的执行成功与否不会影响另一个线程的执行结果
    public static void method03() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        threadPool.schedule(() -> {
            log.debug("任务一执行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, 1, TimeUnit.SECONDS);
        threadPool.schedule(() -> {
            log.debug("任务二执行");
        }, 1, TimeUnit.SECONDS);
    }
}

任务调度线程池的其他用法

任务调度线程池还可以利用scheduleAtFixedRate来实现定时且周期性的执行某些任务

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j(topic = "ch.ThreadPoolTest07")
public class ThreadPoolTest07 {
    static Runnable task01 = new Runnable() {
        @Override
        public void run() {
            log.debug("执行");
        }
    };
    static Runnable task02 = new Runnable() {
        @Override
        public void run() {
            log.debug("执行");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    };

    public static void main(String[] args) {
//        method01();
        method02();
    }

    public static void method01() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        //传入的是task01,预期就是准备一秒后开始执行,然后每隔一秒执行一次
        log.debug("准备周期性运行");
        threadPool.scheduleAtFixedRate(task01, 1, 1, TimeUnit.SECONDS);
    }

    public static void method02() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        //传入的是task01,预期就是准备一秒后开始执行,然后每隔两秒执行一次,这是因为任务执行时间大于间隔时间,所以间隔变化了两秒
        log.debug("准备周期性运行");
        threadPool.scheduleAtFixedRate(task02, 1, 1, TimeUnit.SECONDS);
    }
}

整个线程池的线程数固定,当任务数多于线程数时,会放入无界队列排队,任务执行完毕,这些线程也不会被释放,可以用来周期性的执行一些任务。

线程池处理异常的方式

一般有两种方式,第一种是主动的捕捉异常;第二种是使用future获取异常

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Slf4j(topic = "ch.ThreadPoolTest08")
public class ThreadPoolTest08 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        method1();
        method2();
    }
    
    //主动捕捉异常
    public static void method1() {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(() -> {
            log.debug("任务一");
            try {
                int i = 1 / 0;
            } catch (Exception e) {
                log.error("error:" + e);
            }
        });
    }

    //使用future获取异常
    public static void method2() throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<Boolean> result = pool.submit(() -> {
            log.debug("task1");
            int i = 1 / 0;
            return true;
        });
        log.debug("result:{}", result.get());
    }
}

应用之定时任务

假设有一个需求是要在每周一下午五点执行一个任务,如何利用线程池来实现呢?

import lombok.extern.slf4j.Slf4j;

import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//如何在每周二下午17:00五点定时执行一个任务
@Slf4j(topic = "ch.ThreadPoolTest09")
public class ThreadPoolTest09 {
    static final int beginHour = 20;
    public static void main(String[] args) {
        ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1);
        //任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                log.debug("任务执行");
            }
        };
//        初始间隔时间,有两种情况
//        第一种情况是当前时间在周二下午五点之前
//        第二种情况是当前时间在周二下午五点之后
        LocalDateTime now = LocalDateTime.now();
        //返回当周的周二下午五点
        LocalDateTime time = now.withHour(beginHour).withMinute(0).withSecond(0).withNano(0).with(DayOfWeek.WEDNESDAY);
        //在周二下午五点之前的情况
        if (now.compareTo(time) > 0) {
            time = time.plusWeeks(1);
        }
        long initialDelay = Duration.between(now, time).toMillis();
        //一周的时间
        long gap = 1000 * 60 * 60 * 24 * 7;
        scheduledPool.scheduleAtFixedRate(task, initialDelay, gap, TimeUnit.MILLISECONDS);
    }
}
程序员灯塔
转载请注明原文链接:并发学习记录16:任务调度线程池
喜欢 (0)
违法和不良信息举报电话:022-22558618 举报邮箱:dljd@tidljd.com