Executor作为现在线程的一个管理工具,就像管理线程的管理器一样,不用像以前一样,通过start来开启线程

Executor将提交线程执行线程分离开来,使得用户只需要提交线程,并不需要在乎怎么和什么时候开启线程

需要有以下功能:

1.查看现在开启了哪些进程

2.查看还有哪些进程未执行

3.查看现在开启线程的数量

4.查看还有多少线程未开启

5.设置执行顺序(先提交先执行,先提交后执行)

6.限制最大同时开启线程的个数

7.目前提交的线程执行完之后,关闭管理器(此过程中不允许再提交线程)

8.立即关闭管理器(正在执行的线程也立即停止)


实现原理

Executor管理器将提交上来的线程放入线程等待区(一个LinkedList),当线程执行区中有空位时,控制线程1就会将线程等待区中的线程移除转移到线程执行区(一个LinkedList)。接着,控制线程2就会开启线程执行区中未开启的线程(start)。等到线程执行区中的线程跑完了,控制线程3就会把它从线程执行区移除出去


代码实现

import java.util.*;
import java.util.concurrent.*;
public class MyExecutor{
//静态常量用于决定执行顺序
public static final String FIFO="FIFO";//先进先出
public static final String LIFO="LIFO";//后进先出
//public static final int PRIORITY=2;//优先级
//建立线程池
private LinkedList<Thread> waitinglist;
private String order;//储存指定顺序
private int maxThreadRun;//储存最大并发运行线程数量
//建立执行队列
private LinkedList<Thread> runningList;
//建立三个线程来控制Executor的运行
private Thread checkThread;
private Thread readyThread;
private Thread runThread;
//建立一个标记用来控制管理器的开关
private boolean isShutdown=false;//关闭为true,开启为false
private boolean isShutdownNow=false;//关闭为true,开启为false //构造函数
public MyExecutor()throws IllegalArgumentException{
this(MyExecutor.FIFO,10);//默认执行顺序为先进先出,默认最大并行线程数量为10
} public MyExecutor(String order)throws IllegalArgumentException{
this(order,10);//默认最大并行线程数量为10
} public MyExecutor (String order,int maxThreadRun) throws IllegalArgumentException{
if((order.equals(FIFO)||order.equals(LIFO))&&maxThreadRun>=1){
this.order=order;
waitinglist=new LinkedList<Thread>();
this.maxThreadRun=maxThreadRun;
runningList=new LinkedList<Thread>();
checkThread=new CheckThread(this);
readyThread=new ReadyThread(this);
runThread=new RunThread(this);
checkThread.start();
readyThread.start();
runThread.start();
}
else{
throw new IllegalArgumentException("参数order只能为'FIFO'或'LIFO'");
}
} //提交任务
public void execute(Thread task)throws RejectedExecutionException{
if(state==true){
waitinglist.offer(task);
//count++;
}
else{
throw new RejectedExecutionException("管理器已经关闭,不能再提交任务");
}
}
public void execute(Runnable command)throws IllegalArgumentException,RejectedExecutionException{
this.execute(new Thread(command));
} //将线程池的任务送进执行队列
void ready(){
while(runningList.size()<maxThreadRun){
if(order.equals(FIFO)){
if(waitinglist.peekFirst()!=null)
runningList.offer(waitinglist.pollFirst());
}
else{
if(waitinglist.peekLast()!=null)
runningList.offer(waitinglist.pollLast());
}
}
} //将执行队列中的线程start
void go(){
try{
for(Thread thread:runningList){
if(thread.getState()==Thread.State.NEW){
thread.start();
}
}
}
catch(Exception e){
//e.printStackTrace();
}
} //检测已经结束的线程
void isTerminated(){
try{
for(Thread thread:runningList){
if(thread.getState()==Thread.State.TERMINATED){
runningList.remove(thread); }
}
}
catch(Exception e){
//e.printStackTrace();
}
} //启动一次顺序关闭,执行以前提交的任务,但不接受新任务
public void shutdown(){
isShutdown=true;
} //试图停止所有正在执行的活动任务
public void shutdownNow(){
isShutdownNum=true;
for(Thread thread:waitinglist){
thread.interrupt();
}
for(Thread thread:runningList){
thread.interrupt();
}
} //获取管理器状态
public boolean isShutdown(){
return isShutdown;
}
public boolean isShutdownNow(){
return isShutdownNow;
} //获取正在等待的线程名字
public String getWaitingThreadName(){
return waitinglist.toString();
} //查看现在在执行的线程的名字
public String getRunningThreadName(){
return runningList.toString();
} //查看还有多少个线程在等待
public int getWaitingThreadNum(){
return waitinglist.size();
} //获取正在执行的线程数量
public int getRunningThreadNum(){
return runningList.size();
} //查看管理器中的线程空了没有
public boolean isEmpty(){
return (getWaitingThreadNum()==0&&getRunningThreadNum()==0)?true:false;
}
}
//三个控制线程的代码
//建立一个线程用来检测runningList中的线程是否已经结束了
class CheckThread extends Thread{
private MyExecutor executor;
CheckThread(MyExecutor executor){
this.executor=executor;
setName("CheckThread");
this.setPriority(MAX_PRIORITY);//将线程优先级设置到最高
}
public void run(){
while(!(executor.isShutdown()&&executor.getWaitingThreadNum==0)&&!executor.isShutdownNow()){//当管理器关闭就跳出循环
executor.isTerminated();
Thread.yield();
}
}
} //建立一个线程用来将提交的线程送进执行队列
class ReadyThread extends Thread{
private MyExecutor executor;
ReadyThread(MyExecutor executor){
this.executor=executor;
setName("ReadyThread");
this.setPriority(MAX_PRIORITY);//将线程优先级设置到最高
}
public void run(){
while(!(executor.isShutdown()&&executor.getWaitingThreadNum==0)&&!executor.isShutdownNow()){//当管理器关闭就跳出循环
executor.ready();
Thread.yield();
}
}
} //建立一个线程用来将执行队列中的线程开启
class RunThread extends Thread{
private MyExecutor executor;
RunThread(MyExecutor executor){
this.executor=executor;
setName("RunThread");
this.setPriority(MAX_PRIORITY);//将线程优先级设置到最高
}
public void run(){
while(!(executor.isShutdown()&&executor.getWaitingThreadNum==0)&&!executor.isShutdownNow()){//当管理器关闭就跳出循环
executor.go();
Thread.yield();
}
}
}

几个需要解释的地方

如何控制执行顺序?

首先执行顺序在初始化的时候就需要确定,然后设置一个变量order把这个顺序储存起来

下面看看实现的代码

        //将线程池的任务送进执行队列
void ready(){
while(runningList.size()<maxThreadRun){
if(order.equals(FIFO)){
if(waitinglist.peekFirst()!=null)
runningList.offer(waitinglist.pollFirst());//如果是先进先出,取出等待区中第一个线程
}
else{
if(waitinglist.peekLast()!=null)
runningList.offer(waitinglist.pollLast());//如果是后进先出,取出等待区中最后一个线程
}
}
}

从代码上看,执行顺序实际上是在,将线程从等待区中取出到执行区的过程中控制的

先判断order,然后使用不同的poll方法(pollFirst或者是pollLast)

怎么限制最大同时开启线程的个数?

最大同时开启线程的个数也是在实例化管理器对象的时候就需要确定的(否则,默认的最大同时开启线程的个数为10个)

然后,将设置的值储存在变量maxThreadRun中

下面看看代码怎么实现

        //将线程池的任务送进执行队列
void ready(){
while(runningList.size()<maxThreadRun){//当执行区的大小小于最大可同时运行线程的数量时,才能放的进

从代码上看出,实际上也是将线程从等待区中取出到执行区的过程中控制的

为什么要有一个线程来将结束的线程移除出执行区?

因为!!!当执行区中的线程跑完了之后,这个线程对象仍然是在执行区中存在的,所以如果不把结束的线程移除出去,那么提交任务几毫秒后,执行区就会爆满了,不清理的话,等待区的线程也进不来


几个需要注意的地方

转移线程的时候要判断线程是否为空

代码位置:将线程从等待区中取出到执行区中的过程

//FIFO的情况
if(waitinglist.peekFirst()!=null)//等待区第一个位置的线程不能为空
runningList.offer(waitinglist.pollFirst());//如果是先进先出,取出等待区中第一个线程
//LIFO的情况
if(waitinglist.peekLast()!=null)//等待区最后一个位置的线程不能为空
runningList.offer(waitinglist.pollLast());//如果是后进先出,取出等待区中最后一个线程

为什么不能将空线程放进执行区呢?

因为这样子,空线程在执行区中start和判断这个线程是否结束的时候(getState()==Thread.State.TERMINATED),会抛出NullPointerException空指针异常,会无缘无故占领了执行区的空间,抛出异常和处理异常也会浪费时间

而且不知道为什么,如果不判断的话,会发生阻塞

我想了想,想到了一个不靠谱的解释:

在主线程提交线程给executor之前,executor一直在把空的线程丢进执行区,然后执行区一直在处理异常,等待区也一直在把空线程丢给执行区,这样子也就没有现象出现

可是这样的话,迟早也会有现象出现的,不可能一直都阻塞在那里啊??

遍历线程的容器会抛出ConcurrentModificationException异常

ConcurrentModificationException这个异常是什么呢?

当遍历线程的容器时,会发生这个异常

这个异常存在的意义是不要我们遍历线程的容器,因为如果对装有线程的容器发生修改(比如,移除啊),就会使得线程没有执行

下面看看API的解释:

某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改* Collection*。通常在这些情况下,迭代的结果是不确定的

API很粗暴的,只要循环体中或者迭代器中,遍历的是Collection的时候,就会直接抛出这个异常

所以当开发的时候,没有对容器线程做出修改,那么直接处理忽视掉这个异常吧

线程一定要适当的yield()切换线程

yield()这个方法的用处是:暂停正在执行的线程,切换给别的线程跑跑

如果不用这个方法的话,会出现阻塞

正在执行的那个线程不放cpu,其他的线程也就执行不到了

可是这样子也不会发生阻塞啊,只是运行的慢一点而已

主线程不能轻易的修改执行优先级

我发现,当把主线程(main线程)的优先级改到最低或者较低,很容易出现阻塞

这是为什么捏??

当把可同时开启的线程数量调到1或2

此时又会发生阻塞了

为什么呢?

我想想的是,这样子,控制线程就需要频繁的从等待区中取出线程,也要频繁的将执行区的已结束的线程移除出去

可是这样子也不会发生阻塞啊,只是运行的慢一点而已

真烦!!

模拟Executor策略的实现的更多相关文章

  1. scrapy基础知识之 scrapy 三种模拟登录策略:

    注意:模拟登陆时,必须保证settings.py里的 COOKIES_ENABLED (Cookies中间件) 处于开启状态 COOKIES_ENABLED = True或 # COOKIES_ENA ...

  2. JavaScript设计模式之策略模式(学习笔记)

    在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...

  3. C# WebBrowser HttpWebRequest Cookie 的结合运用。

    在WebBrowser下对网页进行操作其实是一件挺轻松的事情,他可以很方便实现自定义的网站访问习惯.而WebBrowser毕竟是对MS原生 控件的封装,当我们使用C#下的WebBrowser尤其是这样 ...

  4. javascript设计模式之解释器模式详解

    http://www.jb51.net/article/50680.htm 神马是“解释器模式”? 先翻开<GOF>看看Definition:给定一个语言,定义它的文法的一种表示,并定义一 ...

  5. 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)

    强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...

  6. 金融量化分析【day112】:初识量化交易

    一.摘要 为什么需要量化交易? 量化交易是做什么? 量化交易的价值何在? 做量化交易需要什么? 聚宽是什么? 零基础如何快速入门量化交易? 自测与自学 二.量化交易比传统交易强多少? 它能让你的交易效 ...

  7. 深入浅出UE4网络

    UE4中的官方文档结构比较混乱,且有部分错误,不方便学习.笔者试图通过本文,整理出一篇关于UE4网络的文章,方便朋友们对UE4中的网络同步部分的认识,并有进一步理解.如有讲得不清楚明白的地方,还望批评 ...

  8. 20172328 2018-2019《Java软件结构与数据结构》第六周学习总结

    20172328 2018-2019<Java软件结构与数据结构>第六周学习总结 概述 Generalization 本周学习了第十章:非线性集合与数据结构--树.主要讨论了树的使用和实现 ...

  9. spark核心优化详解

    大家好!转眼又到了经验分享的时间了.吼吼,我这里没有摘要也没有引言,只有单纯的经验分享,请见谅哦! 言归正传,目前在大数据领域能够提供的核心计算的工具,如离线计算hadoop生态圈的mr计算模型,以及 ...

随机推荐

  1. 解决TryUpdateModel对象为空的问题

    MVC中的TryUpdateModel确实给开发带来不少便利,但是如果绑定在View上的文本控件没有填写值的时候,再执行TryUpdateModel对应的实体属性就会为空. 如果数据库中对应的字段不允 ...

  2. sql server 链接到本地实例出错

    我在使用VS2010测试package的时候,突然发现sql server 链接到本地实例出错,出错信息如下: “ A network-related or instance-specific err ...

  3. 领会CSS,实际中的研究

    虽懂却不会,真是可怕,自认懂却了无. 善用CSS属性选择器 在用于区别和唯一的情况下完全可以使用属性选择器,而没有必要使用class或id p[city="http://www.css.co ...

  4. AJAX文件上传实践与分析,带HTML5文件上传API。

    对于HTML5已经支持AJAX文件上传了,但如果需要兼容的话还是得用一点小技巧的,HTML5等等介绍,先来看看以前我们是怎么写的. 网上可能会有一些叫AJAX文件上传插件,但在AJAX2.0之前是不可 ...

  5. transform你不知道的那些事

    transform是诸多css3新特性中最打动我的,因为它让方方正正的box module变得真实了. transform通过一组函数实现了对盒子大小.位置.角度的2D或者3D变换.不过很长时间内,我 ...

  6. Android之自动文本输入识别提示

    相信大家都熟悉自动识别提示吧,在我们的生活中随处可见,今天就让我为大家简单介绍一下它是如何设计的. 所谓自动识别输入即是根据用户输入的已有信息,为用户提示可能的值,方便用户完成输入.在Android设 ...

  7. poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)

    /* 题目大意:有N个cows, M个关系 a->b 表示 a认为b popular:如果还有b->c, 那么就会有a->c 问最终有多少个cows被其他所有cows认为是popul ...

  8. Kruskal算法(一)之 C语言详解

    本章介绍克鲁斯卡尔算法.和以往一样,本文会先对克鲁斯卡尔算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 最小生成树 2. 克鲁斯卡尔算法介绍 3 ...

  9. js让程序暂停一段时间再执行

    function sleep(d){ for(var t = Date.now();Date.now() - t <= d;); } sleep(5000); //当前方法暂停5秒

  10. Android基于mAppWidget实现手绘地图(十六)–处理一次触摸多个地图对象

    最好的处理方式就是弹出一个对话框,将用户触摸过的控件罗列出来.你可以通过实现OnMapTouchListener来处理. 参考以下代码,实现上述功能: mapWidget.setOnMapTouchL ...