JAVA并发编程——线程池(二)

使用线程执行器执行有返回值的线程

有返回值的线程定义需要继承接口Callable,然后将线程交给执行器执行,提交线程需要使用submit方法。
例如,某驾校有4辆考试用车,有8个学生要进行考试,要求分别获得考生的考试时间。在本例中,可以将驾校看作是一个线程池,4辆车看作是可以同时执行任务的线程,8个考生看作是线程要执行的对象。
代码示例:

public class Student implements Callable<Long> {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public Long call() throws Exception {
        System.out.println(name + "开始考试");
        long start = System.currentTimeMillis();
        int num = (int) (Math.random() * Integer.MAX_VALUE);
        for(int i = 0; i < num;  i++);
        long end = System.currentTimeMillis();
        System.out.println(name + "考试完毕");
        long time = end - start;
        return time;
    }

    public String getName() {
        return name;
    }
}

public class TestRoom {
    ExecutorService testPool;
    Map<Student, Future<Long>> stuToTimeMap;

    public TestRoom() {
        testPool = Executors.newFixedThreadPool(4);
        stuToTimeMap = new HashMap<>();
    }

    public void test(Student student){
        Future<Long> result = testPool.submit(student);
        stuToTimeMap.put(student, result);
    }

    public Long getTime(Student student){
        Future<Long> result = stuToTimeMap.get(student);
        try {
            return result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return null;
    }
    public void endTest(){
        if(!testPool.isShutdown()){
            testPool.shutdown();
        }
    }
}
public class Test {
    public static void main(String[] args) {
        int stuNum = 8;
        List<Student> stuList = new ArrayList<>();
        TestRoom testRoom = new TestRoom();
        for (int i = 0; i < stuNum; i++){
            Student student = new Student("考生" + (i+1));
            testRoom.test(student);
            stuList.add(student);
        }
        for(Student stu : stuList){
            System.out.println(stu.getName() + "考试用时为:"+ testRoom.getTime(stu)+"毫秒");
        }
        testRoom.endTest();
    }
}

延迟执行、周期执行的执行器

如果要在某一时间之后执行线程操作,或者周期性地重复执行线程操作,则可以使用工厂类Executors的方法newScheduledThreadPool或方法newSingleThreadExecutor。方法newScheduledThreadPool使用给定数目的线程来调度执行任务,而方法newSingleThreadExecutor在一个单独的线程中调度执行任务。这两个方法都将返回一个ScheduledExecutorService线程池对象。

接口ScheduledExecutorService

接口ScheduledExecutorService从接口ExecutorService继承而来,可用于在给定的延迟后运行某个任务,或周期性的执行某个任务。
有关提交定时任务的四个方法:

//提交一个仅仅执行一次的任务
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

创建一个一性执行动作,该执行将在给定的时间延迟后启动

//提交一个仅仅执行一次的任务,有返回值
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

创建一个ScheduledFuture对象,该对象将在给定的延迟后调用callable

// 固定时间间隔循环的执行
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,     long period, TimeUnit unit)

创建并执行一个周期性执行的动作command,首次运行在一个给定的时间延迟initialDelay后启用,随后操作将随着周期period而周期执行

// 固定延时间隔的循环的执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,  long delay, TimeUnit unit);

创建并执行一个周期性执行的动作command,首次运行在一个给定的时间延迟initialDelay后启用,随后操作将在该次结束和下次开始之间有一个固定的延迟delay

接口ScheduledFuture

接口ScheduledExecutorService的4个方法都将返回ScheduledFuture对象。ScheduledFuture也是一个接口 ,他从接口Future和Delayed继承而来,表示一个延迟的、结果可接收的操作。

public interface ScheduledFuture<V> extends Delayed, Future<V> {
}

下面通过几个例子演示上面介绍的几个类和接口的使用方法。
监控某一个设备的工作温度,当温度超多10后,没隔一秒钟发出一次报警提示,如果联连续发出10次报警后,仍没有被处理,则停止设备的运行。
通过线程模拟实际问题。设置两个线程 ,一个线程表示设备,设备在运行的过程中,温度将不断升高;另一个线程表示一个监视系统,用于监控设备的运行情况。在主程序中采用方法scheduleAtFixedRate调度这两个线程,当设备温度超过10时发出警告,当警告超过10次时,采用方法cancel或者shutdown关闭设备。

代码示例:

public class Machine implements Runnable {
    private int temperature;

    public Machine(int temperature) {
        this.temperature = temperature;
    }

    @Override
    public void run() {
        perform();
        temperature++;
        System.out.println("机器的工作温度在升高,当前温度:" + temperature);
    }

    private void perform(){
        int temp = (int) (Math.random() * Integer.MAX_VALUE);
        int sum = 0;
        for(int i = 0; i < temp; i++)
            sum +=i;
    }

    public int getTemperature() {
        return temperature;
    }
}

public class Monitor implements Runnable {
    private Machine machine;
    ScheduledExecutorService scheduledExecutorService;
    static int n = 0;

    public Monitor(Machine machine, ScheduledExecutorService scheduledExecutorService) {
        this.machine = machine;
        this.scheduledExecutorService = scheduledExecutorService;
    }

    @Override
    public void run() {
        if (machine.getTemperature() >= 10) {
            System.out.println("警告:机器温度过高");
            n++;
        }
        if (n > 10) {
            System.out.println("提醒次数限制已到,终止任务");
            scheduledExecutorService.shutdown();
        }
    }
}

public class Test {
    public static void main(String[] args) {

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        Machine machine = new Machine(0);
        Monitor monitor = new Monitor(machine, scheduledExecutorService);
        scheduledExecutorService.scheduleAtFixedRate(machine, 1,2, TimeUnit.SECONDS);
        scheduledExecutorService.scheduleAtFixedRate(monitor,0,1,TimeUnit.SECONDS);
    }
}
机器的工作温度在升高,当前温度:1
机器的工作温度在升高,当前温度:2
机器的工作温度在升高,当前温度:3
机器的工作温度在升高,当前温度:4
机器的工作温度在升高,当前温度:5
机器的工作温度在升高,当前温度:6
机器的工作温度在升高,当前温度:7
机器的工作温度在升高,当前温度:8
机器的工作温度在升高,当前温度:9
机器的工作温度在升高,当前温度:10
警告:机器温度过高
警告:机器温度过高
机器的工作温度在升高,当前温度:11
警告:机器温度过高
警告:机器温度过高
机器的工作温度在升高,当前温度:12
警告:机器温度过高
警告:机器温度过高
机器的工作温度在升高,当前温度:13
警告:机器温度过高
警告:机器温度过高
机器的工作温度在升高,当前温度:14
警告:机器温度过高
警告:机器温度过高
机器的工作温度在升高,当前温度:15
警告:机器温度过高
提醒次数限制已到,终止任务

scheduleAtFixedRate定时任务超时问题

设置任务间隔时间 60s
第一个任务 80s ;第二个任务 20s 第三个任务 50s
第一个任务 0 —80s
第二个任务 80s—100s(上次任务已超时,本次不会再等待60s,会马上开始),
第三个任务 140s-190s
第四个任务 200s开始

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值