模拟Executor策略的实现
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策略的实现的更多相关文章
- scrapy基础知识之 scrapy 三种模拟登录策略:
注意:模拟登陆时,必须保证settings.py里的 COOKIES_ENABLED (Cookies中间件) 处于开启状态 COOKIES_ENABLED = True或 # COOKIES_ENA ...
- JavaScript设计模式之策略模式(学习笔记)
在网上搜索“为什么MVC不是一种设计模式呢?”其中有解答:MVC其实是三个经典设计模式的演变:观察者模式(Observer).策略模式(Strategy).组合模式(Composite).所以我今天选 ...
- C# WebBrowser HttpWebRequest Cookie 的结合运用。
在WebBrowser下对网页进行操作其实是一件挺轻松的事情,他可以很方便实现自定义的网站访问习惯.而WebBrowser毕竟是对MS原生 控件的封装,当我们使用C#下的WebBrowser尤其是这样 ...
- javascript设计模式之解释器模式详解
http://www.jb51.net/article/50680.htm 神马是“解释器模式”? 先翻开<GOF>看看Definition:给定一个语言,定义它的文法的一种表示,并定义一 ...
- 强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods)
强化学习读书笔记 - 05 - 蒙特卡洛方法(Monte Carlo Methods) 学习笔记: Reinforcement Learning: An Introduction, Richard S ...
- 金融量化分析【day112】:初识量化交易
一.摘要 为什么需要量化交易? 量化交易是做什么? 量化交易的价值何在? 做量化交易需要什么? 聚宽是什么? 零基础如何快速入门量化交易? 自测与自学 二.量化交易比传统交易强多少? 它能让你的交易效 ...
- 深入浅出UE4网络
UE4中的官方文档结构比较混乱,且有部分错误,不方便学习.笔者试图通过本文,整理出一篇关于UE4网络的文章,方便朋友们对UE4中的网络同步部分的认识,并有进一步理解.如有讲得不清楚明白的地方,还望批评 ...
- 20172328 2018-2019《Java软件结构与数据结构》第六周学习总结
20172328 2018-2019<Java软件结构与数据结构>第六周学习总结 概述 Generalization 本周学习了第十章:非线性集合与数据结构--树.主要讨论了树的使用和实现 ...
- spark核心优化详解
大家好!转眼又到了经验分享的时间了.吼吼,我这里没有摘要也没有引言,只有单纯的经验分享,请见谅哦! 言归正传,目前在大数据领域能够提供的核心计算的工具,如离线计算hadoop生态圈的mr计算模型,以及 ...
随机推荐
- Javascript快速入门(上篇)
Javascript的熟练之路,小弟来了. JavaScript简介:JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript ...
- PC端和移动端地址适配
判断当前页面的打开方式是pc还是移动设备,如果是移动设备,跳转到对应移到端网站的方法: 方法一.还是用@media screen 思路:css使用媒体查询,当屏幕小于760px时,使某个元素的样式发生 ...
- 抛弃强大的TFS ,借助于BugTracker.NET + Visual Source Safe + SourceLink搭建项目开发环境
微软公司的Team Foundation Server是个强大的项目管理工具,如果用.NET开发,它应该是首选的项目管理平台.TFS的成本比较高,而且和Visual Studio集成紧密.比如TSF有 ...
- js中如果省略分号那么它是如何运行的
在javascript工作中,我们几乎不会去省略分号:为了不必要的麻烦以及代码的规范,那么如果我们省略:会发生呢?预知详情请听下回分解. 看代码! 片段一: 1 var a 2 = 3 8 4 con ...
- 【WP 8.1开发】电子罗盘
罗盘,估计也不用我过多介绍,学过初中物理的都知道,不管是指南针,还是指北针,其本质就是用来辨别方向的. 操作电子罗盘伟感器也不复杂,主要就是两个角度: 1.当前方向与磁北的夹角: 2.当前方向与地北的 ...
- 【WP 8.1开发】如何处理摄像头翻转的问题
模拟器就像我们儿时的梦境,在其上运行应用程序时,一切总是那么美好的:而真机测试如同我们这个纷乱无章的现实世界,你会遇到各种小人和畜生,常常会遭受莫名的挫折.面对挫折,有人迎难而上,或不予理采,走自己的 ...
- 史上最全github使用方法:github入门到精通
[初识Github]首先让我们大家一起喊一句“Hello Github”.YEAH!就是这样. 原文 http://www.eoeandroid.com/thread-274556-1-1.htmlG ...
- Cocos2d-x 3.2 学习笔记(二)创建自定义项目
一.通过命令创建项目 前面搭建好环境后,怎样创建自己的cocos2d-x项目呢? 先来看看cocos2dx 3.2的目录吧(涉及到3.1.1版本的,请自动对应3.2版本,3.x版本的环境搭建都是一样的 ...
- javascript必知必会:面象对象编程
面象对象编程技术的核心理念:封装.继承.多态:在一些主流的高级编程语言中,比如:C#,VB.NET,JAVA,PHP等都是很容易实现的,而如果要在javascript中实现面象对象编程,可就不那么直接 ...
- 两种读写配置文件的方案(app.config与web.config通用)
第一种方法:采用MS现有的ConfigurationManager来进行读写 using System.Configuration; namespace Zwj.TEMS.Common { publi ...