文档章节

自己动手写一个线程池

tongqu
 tongqu
发布于 2016/02/15 23:17
字数 1469
阅读 44
收藏 2
点赞 1
评论 0

基础知识:

    http://blog.csdn.net/i_lovefish/article/details/8042708

一。ExcutorService和ThreadGroup的比较

    在使用线程池的时候,大多数情况我们会使用excutors类来创建一个线程池。过程不再赘述。但我们发现,ExcutorService中对线程的操作只有submit、shutdown、shutdownnow等寥寥几个方法。对线程的管理并不细致。而ThreadGroup类中,有很多诸如获取存活线程数量、获取所有线程、中断线程等诸多方法。所以我们考虑用ThreadGroup来模拟一个线程池。

二。通过继承ThreadGroup模拟一个线程池

public class ThreadPoolTest extends ThreadGroup{
    //线程池是否开启
    private boolean isAlive;
    //线程池中的任务队列
    private LinkedList taskQueue;
    //线程池中的线程id
    private int threadID;
    //线程池的id
    private static int threadPoolId;
    public  ThreadPoolTest(int numThreads) {
        super("ThreadPool--"+numThreads);
        //设置为守护线程,表示当线程池中的所有线程都销毁时(interrupt方法执行),该线程池自动销毁
        super.setDaemon(true);
        this.isAlive=true;
        this.taskQueue=new LinkedList(); 
        for(int i=0;i<numThreads;i++){
            //TODO 声明一些线程,并让这些线程运行
        }
    }
  }

这里的注释TODO的部分很重要,需要做的功能如下:

  1. 当任务队列为null时,我们需要让我们声明的线程等待s。

  2. 当任务队列中不为null是,我们需要唤醒我们的线程,拿到任务队列中的任务去执行。

综上,我们需要new一些个性化的线程,让这些线程去任务队列里取任务线程,根据队列的情况执行不同操作。

方便起见,我们直接在ThreadPoolTest里写一个继承了Thread的内部类,Todo部分改为          

new PooledThread().start();

private class PooledThread extends Thread{
        public PooledThread(){
            super(ThreadPoolTest.this,"PooledThread--"+(threadID++));
            
        }
        @Override
        public void run() {
            //如果该线程没有被终止
            while(!isInterrupted()){
                Runnable task=null;
                try {
                    task=getTask();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(task==null){
                    //之所以没有用wait()是因为wait和notify需要在同步代码块或者同步方法中执行
                    //之所以没用task.wait()是因为task为null
                    return;
                }
                try {
                    task.run(); 
                } catch (Throwable e) {
                    e.printStackTrace();
                    //当线程族中的线程有未被捕获的异常时,jvm会去调用uncaughtException方法
                    //这个方法也是threadGroup的一个特色
                    uncaughtException(this, e);
                }
            }
        }
    }

上述代码中涉及到了getTask方法,该方法应该写在ThreadPoolTest中,用来获取任务队列中的线程

protected synchronized Runnable getTask() throws InterruptedException{
        while(this.taskQueue.size()==0){
            if(!this.isAlive){
                return null;
            }
            wait();//wait在这那!!说明如果队列中没有任务,则一直处于阻塞状态
        }
        return (Runnable) this.taskQueue.removeFirst();
    }

下面开始实现线程池的submit方法,往队列中添加任务,唤醒getTask

//添加新任务
    public synchronized void submit(Runnable task){
        if(!this.isAlive){
            throw new IllegalStateException();
        }
        if(task!=null){
            //将工作线程放到任务队列的尾部
            this.taskQueue.add(task);
            //通知工作线程取任务
            notify();//getTask被唤醒啦,构造方法中new出来的线程由阻塞状态进入可运行状态了
        }        
    }

重点方法介绍完毕,我们再加一些shutdown,shutdownnow方法就简单多了

    

public synchronized void shutdownNow(){
        if(isAlive){
            this.isAlive=false;
            this.taskQueue.clear();
        }
        //终止线程池中的所有线程
        this.interrupt();
    }
    //关闭线程池,并等待线程池中的所有任务被运行完,但不能接受新的任务
    public void shutdown(){
        synchronized (this) {
            isAlive=false;
            notifyAll();
        }
        
        //将线程池中的活动线程拷贝到新创建的线程数组thread中
        Thread[] threads=new Thread[this.activeCount()];
        //将线程池中活动的线程拷贝到新创建的线程数组中
        int count=this.enumerate(threads);
        for(int i=0;i<count;i++){
            try {
                //等待所有线程执行结束
                threads[i].join();
            } catch (InterruptedException e) {
                     e.printStackTrace();
            }
        }
        System.out.println("所有线程运行完毕");
    }

全部代码如下

package com.me.threadtest;

import java.util.LinkedList;


public class ThreadPoolTest extends ThreadGroup{
    //线程池是否开启
    private boolean isAlive;
    //线程池中的任务队列
    private LinkedList taskQueue;
    //线程池中的线程id
    private int threadID;
    //线程池的id
    private static int threadPoolId;
    public  ThreadPoolTest(int numThreads) {
        super("ThreadPool--"+numThreads);
        //设置为守护线程,表示当线程池中的所有线程都销毁时,该线程池自动销毁
        super.setDaemon(true);
        this.isAlive=true;
        this.taskQueue=new LinkedList(); 
        for(int i=0;i<numThreads;i++){
            //TODO 声明一些线程,并让这些线程运行
            new PooledThread().start();
        }
    }
    //添加新任务
    public synchronized void submit(Runnable task){
        if(!this.isAlive){
            throw new IllegalStateException();
        }
        if(task!=null){
            //将工作线程放到任务队列的尾部
            this.taskQueue.add(task);
            //通知工作线程取任务
            //TODO 不懂
            notify();
        }
        
    }
    //获取任务
    protected synchronized Runnable getTask() throws InterruptedException{
        while(this.taskQueue.size()==0){
            if(!this.isAlive){
                return null;
            }
            wait();
        }
        return (Runnable) this.taskQueue.removeFirst();
    }
    public synchronized void shutdownNow(){
        if(isAlive){
            this.isAlive=false;
            this.taskQueue.clear();
        }
        //终止线程池中的所有线程
        this.interrupt();
    }
    //关闭线程池,并等待线程池中的所有任务被运行完,但不能接受新的任务
    public void shutdown(){
        synchronized (this) {
            isAlive=false;
            notifyAll();
        }
        
        //将线程池中的活动线程拷贝到新创建的线程数组thread中
        Thread[] threads=new Thread[this.activeCount()];
        //将线程池中活动的线程拷贝到新创建的线程数组中
        int count=this.enumerate(threads);
        for(int i=0;i<count;i++){
            try {
                //等待所有线程执行结束
                threads[i].join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("所有线程运行完毕");
    }
    private class PooledThread extends Thread{
        public PooledThread(){
            super(ThreadPoolTest.this,"PooledThread--"+(threadID++));
            
        }
        @Override
        public void run() {
            //如果该线程没有被终止
            while(!isInterrupted()){
                Runnable task=null;
                try {
                    task=getTask();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                if(task==null){
//本想通过下面注释掉的代码实现‘队列为null时等待’,但发现实现不了,所以在gettask中实现吧
//                    task.wait();
//                    wait();
                    return;
                }
                try {
                    task.run(); 
                } catch (Throwable e) {
                    e.printStackTrace();
                    //当线程族中的线程有未被捕获的异常时,jvm会去调用uncaughtException方法
                    uncaughtException(this, e);
                }
            }
        }
    }
}

使用的时候,比如我们设置的线程数为5,添加到队列中的线程数为10,那么同时运行的线程只有5个,ExcutorService中还有一个队列的最大长度,这个在上述代码中在添加一个maxSize的属性,并改动少数代码就可以实现了,非常简单,不再赘述。

© 著作权归作者所有

共有 人打赏支持
tongqu
粉丝 39
博文 37
码字总数 26162
作品 0
海淀
自己动手实现简单web容器二

上一篇博客《自己动手实现简单web容器一》,留下了一些问题,今天我们来解决并行访问的问题,即多个用户同时访问,我们的web服务器能给出响应,而不至于阻塞住。现代计算机越来越好,CPU核心...

冷血狂魔
2016/08/21
26
0
java中线程wait,notify,notifyall

有次给客户写一个数据库连接池的插件,由于本人公司的产品是NoSqL数据库,不能和开源的数据库连接池配合使用,所有只有自己动手写 其中有一个场景是这样的:多个线程从一个池子里面拿连接,超...

1987times
2014/04/04
0
0
c#执行定时计算限制操作(计时器)

在.Net Framework Class Library(FCL)中,System.Threading命名空间下定义了一个Timer类,这就是常用的一个计时器。实际上FCL总共提供了如下几种计时器: 1、System.Threading.Timer 在实际...

嗯哼9925
2017/11/22
0
0
堆栈、堆、方法区介绍

堆栈、堆、方法区介绍 终于开始看java啦…不知道有没有很多人跟我一样想法,先把安卓看完了再去看java,因为安卓直接跟工资挂钩而java更多的是内功.直到前段时间我和我们这边后台大佬对接开发w...

zly921112
2017/03/10
0
0
源码分析: PipedInputStream和PipedOutputStream

场景 假设我们需要上传一组动态增加的数据, 输入端可以看作inputSteam, 输入端是outputSteam, 但是输入和输出端不能直接对接, 那么我们要怎样实现呢? 我希望的解决方案时, 输入和输出通过一个...

AssIstne
05/19
0
0
concurrent包 线程池、资源封锁和队列、ReentrantReadWriteLock介绍

jdk1.5后,提供了java.util.concurrent包,它可以实现线程池,你把线程当成普通对象就可以了,它来负责调度和执行 包括两类线程池 固定线程池 可变线程池 延迟线程池 固定线程池 public sta...

JavaGG
2010/03/24
0
0
Java 数据库连接池的技术选型都应考虑哪些要素

数据库连接池是一个牵涉面很广的话题,对于大型系统,数据库连接池的好坏,关系到系统的性能和稳定性,因此,选好数据库连接池,是系统在架构时期的一个重要任务。 一般来讲,Java数据库连接...

webas
2013/04/26
0
0
关于concurrent包 线程池、资源封锁和队列、ReentrantReadWriteLock介绍

jdk1.5后,提供了java.util.concurrent包,它可以实现线程池,你把线程当成普通对象就可以了,它来负责调度和执行 包括两类线程池 固定线程池 可变线程池 延迟线程池 固定线程池 public sta...

JavaGG
2009/06/11
1K
1
完成端口与高性能服务器程序开发

完成端口与高性能服务器程序开发 Email:kruglinskiatgmaildotcom Blog:kruglinski.blogchina.com 早在两年前我就已经能很熟练的运用完成端口这种技术了,只是一直没有机会将它用在什么项目中,...

晨曦之光
2012/03/09
0
0
mysql 自动检查并启动slave 线程的小方法,提高运维效率

问题 某些公司数据库架构是两主多从,双活跨IDC机房,这样就存在一个问题。跨机房的数据库同步,必然会 存在网络抖动,异常引起的slave io,slave sql线程关闭的情况。 2.解决的思虑 1)写一个...

余二五
2017/11/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

微信分享的细节

分享的缩略图要求: 一、图片大小小于32k 二、图片的尺寸为 宽度 :128px 高度:128px 分享title 和 description 出现金额等 以上情况存在会导致触发分享按钮 但是页面没有反应...

Js_Mei
刚刚
0
0
【2018.07.23学习笔记】【linux高级知识 Shell脚本编程练习】

1、编写shell脚本,计算1-100的和; #!/bin/bashsum=0for i in `seq 1 100`do sum=$[$sum+$i]doneecho $sum 2、编写shell脚本,要求输入一个数字,然后计算出从1到输入数字的和,要求...

lgsxp
3分钟前
0
0
xss攻防浅谈

导读 XSS (Cross-Site Script) 攻击又叫跨站脚本攻击, 本质是一种注入攻击. 其原理, 简单的说就是利用各种手段把恶意代码添加到网页中, 并让受害者执行这段脚本. XSS能做用户使用浏览器能做的...

吴伟祥
3分钟前
0
0
js回调的一次应用

function hideBtn(option) { if (option == 1) { $("#addBtn").hide(); $("#addSonBtn").hide(); }}$("body").on("click", "#selectBtn", function () {......

晨猫
9分钟前
0
0
C++_读写ini配置文件

1.WritePrivateProfileString:

一个小妞
9分钟前
0
0
通往阿里,BAT的50+经典Java面试题及答案解析(上)

Java是一个支持并发、基于类和面向对象的计算机编程语言。下面列出了面向对象软件开发的优点: 代码开发模块化,更易维护和修改。 代码复用。 增强代码的可靠性和灵活性。 增加代码的可理解性...

Java大蜗牛
9分钟前
0
0
数据库两大神器【索引和锁】

前言 只有光头才能变强 索引和锁在数据库中可以说是非常重要的知识点了,在面试中也会经常会被问到的。 本文力求简单讲清每个知识点,希望大家看完能有所收获 声明:如果没有说明具体的数据库...

Java3y
13分钟前
0
0
Application Express安装

Application Express安装文档 数据库选择和安装 数据库选择 Oracle建议直接12.2.0.1.0及以上的版本,12.1存在20618595bug(具体可参见官方文档) Oracle 12c 中安装oracle application expr...

youfen
25分钟前
0
0
OpenMessaging概览

序 本文主要研究一下OpenMessaging 架构图 namespace,类似cgroup的namespace,用来进行安全隔离,每个namespace有自己的producer、consumer、topic、queue等 producer,消息生产者有两类,一...

go4it
30分钟前
0
0
MySQL索引类型

MySQL目前主要有以下几种索引类型: 1.普通索引 2.唯一索引 3.主键索引 4.组合索引 5.全文索引 https://www.cnblogs.com/luyucheng/p/6289714.html...

灯下草虫鸣_
31分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部