前言

并发(Concurrency)一直谈论java绕不开的一个话题,从移动开发工程师到后端工程师,几乎所有的面试都要涉及到并发/多线程的一些问题。虽然多数时候我们使用线程池,都是已经实现好的框架——jdk7中就有现成的ThreadPoolExecutor供我们使用,不过,自己实现一个简化的线程池,对于帮助我们理解其内部原理还是有一些帮助的。

设计

核心思想如下:

  • 线程池实例为单例
  • 线程池实例中保存着一个线程数组,用来分发任务
  • 线程池中通过一个BlockingQueue实例,来实现FIFO的任务队列,这个实例同时被线程数组中的每一个线程拥有
  • 线程通过while循环,不断从队列中取出任务执行(Runnable

实现

首先是线程池示例SimpleThreadPool.java,使用单例模式。

public class SimpleThreadPool {
private static SimpleThreadPool ourInstance;
private static final int QUEUE_SIZE = 100; // todo 调整 最优的capacity
private PoolThread[] threadArray;
private BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
private boolean isStopped; public static SimpleThreadPool getInstance() {
if (ourInstance == null) {
ourInstance = new SimpleThreadPool();
}
return ourInstance;
} public SimpleThreadPool initPoolSize(int size) {
if (size < 1 || size > 10) {
throw new RuntimeException("size must be 1~10!");
}
threadArray = new PoolThread[size];
for (int i = 0; i < size; i++) {
threadArray[i] = new PoolThread(blockingQueue);
threadArray[i].start();
}
return this;
} public synchronized SimpleThreadPool execute(Runnable runnable) {
if (isStopped) {
throw new IllegalStateException("Thread Pool is stopped!");
}
blockingQueue.offer(runnable);
return this;
} public synchronized void doStop() {
for (PoolThread pt : threadArray) {
pt.doStop();
}
isStopped = true;
} }

然后是自定义的线程池线程,PoolThread.java

是否需要在run、doStop两个方法前声明synchronized,存疑。

public class PoolThread extends Thread {
private BlockingQueue<Runnable> blockingQueue;
private boolean isStopped; public PoolThread(BlockingQueue<Runnable> blockingQueue) {
this.blockingQueue = blockingQueue;
} @Override
public void run() {
while (!isStopped) {
try {
Runnable runnable = blockingQueue.take();
System.out.println(getName() + " is running...");
runnable.run();
} catch (InterruptedException e) {
// todo: log it
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} public void doStop() {
interrupt();
isStopped = true;
}
}

写个Main函数测试一下。

public class Main {
public static void main(String[] args) {
SimpleThreadPool.getInstance().initPoolSize(10).execute(new Runnable() {
@Override
public void run() {
System.out.println("1+1=" + (1 + 1));
}
}).execute(new Runnable() {
@Override
public void run() {
System.out.println("2+2=" + (2 + 2));
}
}).execute(new Runnable() {
@Override
public void run() {
System.out.println("3+3=" + (3 + 3));
}
}).execute(new Runnable() {
@Override
public void run() {
System.out.println("4+4=" + (4 + 4));
}
}).execute(new Runnable() {
@Override
public void run() {
System.out.println("5+5=" + (5 + 5));
}
})
;
SimpleThreadPool.getInstance().doStop();
}
}

示例输出:

Thread-9 is running...
1+1=2
Thread-9 is running...
2+2=4
Thread-9 is running...
3+3=6
Thread-3 is running...
4+4=8
Thread-4 is running...
5+5=10

SimpleThreadPool实践的更多相关文章

  1. Quartz应用实践入门案例一(基于Web环境)

    Quartz是一个完全由java编写的开源作业调度框架,正是因为这个框架整合了许多额外的功能,所以在使用上就显得相当容易.只是需要简单的配置一下就能轻松的使用任务调度了.在Quartz中,真正执行的j ...

  2. spring-boot-2.0.3之quartz集成,最佳实践

    前言 开心一刻 快过年了,大街上,爷爷在给孙子示范摔炮怎么放,嘴里还不停念叨:要像这样,用劲甩才能响.示范了一个,两个,三个... 孙子终于忍不住了,抱着爷爷的腿哭起来:爷呀,你给我剩个吧! 新的一年 ...

  3. spring-boot-starter-quartz集群实践

    [**前情提要**]由于项目需要,需要一个定时任务集群,故此有了这个spring-boot-starter-quartz集群的实践.springboot的版本为:2.1.6.RELEASE:quart ...

  4. webp图片实践之路

    最近,我们在项目中实践了webp图片,并且抽离出了工具模块,整合到了项目的基础模板中.传闻IOS10也将要支持webp,那么使用webp带来的性能提升将更加明显.估计在不久的将来,webp会成为标配. ...

  5. Hangfire项目实践分享

    Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(De ...

  6. TDD在Unity3D游戏项目开发中的实践

    0x00 前言 关于TDD测试驱动开发的文章已经有很多了,但是在游戏开发尤其是使用Unity3D开发游戏时,却听不到特别多关于TDD的声音.那么本文就来简单聊一聊TDD如何在U3D项目中使用以及如何使 ...

  7. Logstash实践: 分布式系统的日志监控

    文/赵杰 2015.11.04 1. 前言 服务端日志你有多重视? 我们没有日志 有日志,但基本不去控制需要输出的内容 经常微调日志,只输出我们想看和有用的 经常监控日志,一方面帮助日志微调,一方面及 ...

  8. 【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器

    一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从 ...

  9. Windows平台分布式架构实践 - 负载均衡

    概述 最近.NET的世界开始闹腾了,微软官方终于加入到了对.NET跨平台的支持,并且在不久的将来,我们在VS里面写的代码可能就可以通过Mono直接在Linux和Mac上运行.那么大家(开发者和企业)为 ...

随机推荐

  1. 使用java连接MySQL数据库

    import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import com.mys ...

  2. iOS测试常见崩溃

    什么是崩溃日志,从哪里能得它? iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上.崩溃日志上有很多有用的信息,包括应用是什么情况下闪退的.通常,上面有每个正在执行线程的 ...

  3. MySQL初探

    慕课网http://www.imooc.com/learn/122 课程学习笔记     修改MySQL提示符        shell>mysql --prompt 提示符        my ...

  4. JS this指向问题

    <button onclick=(function(){alert(this)})()>I'm button</button>//this指代window <button ...

  5. BHP Net Tool

    #导入需要用到的包 import sys import getopt import threading import socket import subprocess #定义全局变量 listen = ...

  6. 关于SQL Cookbook里dept与emp表结构以及数据

    用MYSQL 写了一下,将number变成int, to_date去掉即可. DROP TABLE IF EXISTS `dept`; CREATE TABLE `dept` ( `DEPTNO` ) ...

  7. win32程序通过LPCREATESTRUCT中的lpCreateParams传递参数给窗口过程函数

    win32窗口程序中如果需要给窗口过程函数传递自定义参数,可以通过LPCREATESTRUCT结构体中的lpCreateParams进行传递. 创建窗口实例函数: m_hWnd = CreateWin ...

  8. [原创]C++通用宏定义

    //单态模式(singletion) #defube DECLEAR_SINGLETION(ClassName) \ private: \ static ClassName* m_pInstance; ...

  9. 在树莓派上部署InfoPi

    如果仅仅想试用InfoPi,请参照此文在Windows上试用.在Windows上部署比在树莓派上部署简单得多. 先说明一下,我用的系统是Raspbian(2014-06-20发布的). 用户pi,工作 ...

  10. 3、C#入门第3课

    1.c#中一个解决方案 里面两个程序 怎么一个启动另一个? 我一个解决方案下,有两个工程,我想让A工程在适当时候,启动B工程,比如A中有个按钮,一点,B工程就启动了. System.Diagnosti ...