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工具。

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

扩展

参考文献

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

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