5-Executor

概述

引入

我们之前介绍了

  • Runnable,Callable类,用来将我们的逻辑代码进行封装
  • Future类,在上面的基础上增加了任务进度的控制及检测
  • FutureTask类,用来兼容我们封装的Callable/Runnable接口差异

我们之前讲的都是对任务的封装和执行效果的检测、控制,但是还有重要的一环没有做——创建子线程执行任务。

我们之前都是在主线程中自行创建线程,执行子任务。这种方法简单粗暴,但是有以下问题:

  1. 重复的、不涉及业务逻辑的代码不便于编程
  2. 频繁创建线程,在性能上有很大的开销
  3. 在极端调用情况下,无法合理利用机器资源【比如同时创建200个线程执行任务,电脑卡死,不一定比创建20个线程复用执行快】

我们今天了解的就是如何管理子线程的创建的接口Executor。此接口最开始设计的目的是将任务提交与任务执行机制分离,从而更方便、科学的管理子任务的执行。

摘要

本文介绍了Executor接口的设计理念,并自行设计了一个Executor的实现类以减少大量创建子任务的编码、执行成本。

类介绍

类定位

设计此类用来将任务提交和任务执行机制分离,即将子线程的来路和子线程的管理从程序员手中取出来,专门实现一套完善、复杂的程序猿好用,资源利用效率还高。

注意

源码解读

/**
 * 稍后执行提交的任务,该任务可能在以下几种线程中执行:
 * 1. 新创建的线程
 * 2. 线程池中的线程
 * 3. 调用此方法的主线程
 *
 * @param command 任务逻辑代码封装成的接口实现类
 * @throws RejectedExecutionException 如果 Executor 拒绝执行此任务抛出的异常
 * @throws NullPointerException 如果入参为空抛出的异常
 */
void execute(Runnable command);

使用示例

示例

package com.gateway.concurrent.pool;

import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * @author lipengcheng3 Created date 2019-02-22 18:18
 */
public class MyThreadExecutor implements Executor {

    private final Thread[] threads = new Thread[5];

    private final BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>();

    public MyThreadExecutor() {
        for (int i = 0; i < 5; i++) {
            threads[i] = new Thread(new MyRunnable());
            threads[i].setDaemon(true);
            threads[i].start();

        }
    }

    @Override
    public void execute(Runnable command) {
        try {
            tasks.put(command);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class MyRunnable implements Runnable {

        private volatile boolean flag = true;

        @Override
        public void run() {
            while (flag) {
                try {
                    Runnable runnable = tasks.take();
                    runnable.run();
                } catch (Exception e) {
                    e.printStackTrace();
                    flag = false;
                }
            }
        }
    }


    public static void main(String args[]) throws IOException, InterruptedException {
        MyThreadExecutor myThreadExecutor = new MyThreadExecutor();
        for (int i = 0; i < 40; i++) {
            myThreadExecutor.execute(new TestCalss("任务" + i));
        }
        Thread.sleep(1000);

    }

    static class TestCalss implements Runnable {

        private String name;

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

        @Override
        public void run() {
            Long sleepTime = (long) (Math.random() * 1000);
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " 完成,用时 " + sleepTime + " 毫秒");
        }
    }

}

核心逻辑介绍

思路很简单我们分三个块介绍:

  1. MyThreadExecutor的运行机制

我们使用一个固定长度为5的数组,创建了5个线程,运行我们定义好的MyRunnable的逻辑:即不停的从一个阻塞队列中取任务执行。

向外暴露的execute方法,用于将任务不停的放入任务的阻塞队列中。

  1. main的运行机制

创建MyThreadExecutor实例后不停的往里放任务,放完后等待1000ms后退出程序。

使用思路

其实Executor的实现思路很简单,我们要着重处理好以下几个问题:

  1. 任务的添加和获取的线程安全性

  2. 一个Thread在创建时。他执行的内容就已经确定了,我们应如何将Runnable任务传给已经创建好的Thread并执行以实现线程复用的问题

  3. 如何处理好线程的回收问题

    这里我就遇到了这个问题,虽然我们在代码中也没有做回收操作。一开始在运行程序时发现执行结束后程序不会退出。想了一下下秒懂,是应为那5个子线程还在跑。于是把那几个线程从用户线程切换为守护线程,这样在主线程也是唯一的用户线程等待1000ms退出后子线程随着jvm的停止自动被回收。

问题

在这里好好想想,怎么搞能写出伸缩性更好、更灵活易用的Executor工具。

其实我上面写的实现已经有点像线程池了,只不过我都是写死并封装的,对用户透明。

扩展

参考文献

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一.线程安全性 线程安全是建立在对于对象状态访问操作进行管理,特别是对共享的与可变的状态的访问 解释下上面的话: ...
    黄大大吃不胖阅读 4,373评论 0 3
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 6,490评论 0 8
  • Java知识点1、==和equals的区别基本类型比较==比较内容 equals比较地址值引用类型比较==比较地址...
    压抑的内心阅读 3,712评论 0 0
  • 原文链接 译者:靖靖 并发 进程和线程 在并发编程当中,有两个基本的执行单元:进程和线程。在java中,我们大部分...
    4b4f3ceb6f71阅读 4,210评论 4 16
  • 导读目录 线程组(ThreadGroup) 线程池(Thread Pool) Fork/Join框架和Execut...
    ql2012jz阅读 5,353评论 0 0

友情链接更多精彩内容