利用Java编写自己的线程池
2017-03-15 17:00
323 查看
前言:
自JDK1.5问世以来JDK的开发者在线程处理方案中提供了很多多线程方面的工具包在java.util.current包下,尤其显著的就是线程池的出现,也算是JDK的一个
里程碑版本;
这里我们仅仅讨论线程池的功能和设计原理我们可以先回到没有线程池的年代,业务开发中出现的一些性能和障碍瓶颈问题?比如在使用Java写网络
服务的时候,通常在服务端会使用一个SocketServer来监听网卡上指定的一个端口(通常端口选择大于1024小于65535,1024之内的端口基本被操作系统
内部分配完毕),客户端根据主机地址和服务进程监听的端口号来与服务进行网络上的通信,在并发量比较小的情况下,通常的设计方案即是客户端发来一
个请求,服务端与之建立一个相应的线程来处理和该客户端之间的数据业务处理,业务处理完毕之后,由于Jvm有自动垃圾回收策略,所以程序员不必自己
考虑线程的回收,无效的线程会被JVM自行回收(一定程度上减少了程序员的工作量,其核心的思想是减轻JVM自身的"重量");可是如果在并发量比较大
的情况下,比如淘宝的双十一活动,在不考虑集群服务的情况下(其实集群也是利用多台服务来分担请求压力), 单点来处理的话这时将会出现灾难,严重
将会导致服务器宕机,那么出现这样的问题是什么原因造成的呢?通常情况下每款操作系统中对每一个进程内部的线程数量是有一定限制的,因为当线程
无限制多的情况下,操作系统(这里先考虑只是在单核的情况下)内部的调度算法去不停的切换线程赋予执行权,这将会导致有些线程会在很长时间内得不到
执行权,而且每一个线程的处理数据都需要单独的开辟一个工作空间,并且操作系统也要对这些线程的处理流程进行监控和记录,可想而知在线程无限多
的情况下,操作系统也不能给予很好的解决,最终出现的问题可能是死机,或者卡死现象,严重将会导致服务关闭;
线程池的出现是是在一定的程度上解决了这些问题,它的设计思想是根据业务的规则,可以先前制定好一个线程池,也就是一定数量的线程(当然这个
数量的线程数是操作系统可以接纳的),当有任务请求而来的时候先放入一个有序的队列中,然后线程池中的线程会去以争抢的方式(这里的争抢其实是无序
的且没有规则的,完全由运行的平台操作系统调度算法来控制的)去把消费任务队列中的任务(这里消费是一个比较关键的过程,)取出进行消
费;这样的处理方式优点是操作系统的压力得以减轻,宕机的现象不会出现;缺点就是这些线程池中的线程去从队列中取出任务时,由于这个任务队列被
所有线程所共享,所以必定会有一个加锁和释放锁的过程,从而导致很多线程得到了CPU赋予的执行权却没有操作业务的执行权,在一定程度上加剧了整
个响应过程的耗时,当然这相比系统宕机问题必然是一个更好的选择;
这里我贴上自己手写的线程池代码;
package com.pactera.com;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class HXMThreadPools {
private int initNum;//初始化线程数量
private List<Runnable> tpl = Collections.synchronizedList(new ArrayList<Runnable>());//创建一个线程池
private LinkedBlockingQueue<Runnable> task = new LinkedBlockingQueue<Runnable>();//任务队列
public HXMThreadPools(int initNum){
this.initNum = initNum;
this.initTPL();
}
public void addTask(Runnable taskTemp){//增加任务
synchronized(task){
this.task.add(taskTemp);
this.task.notifyAll();//唤醒哪些因访问任务队列没有得到任务而导致睡眠的线程兄弟们
}
}
private void initTPL(){
for(int i=0;i<this.initNum;i++){
Runnable ruMain = new Runnable(){
@Override
public void run() {
while(true){//让线程处于永痕的工作状态
Runnable temp = null;
synchronized(task){
while(task.isEmpty()){//如果当前任务列表中无任务
try {
task.wait();//如果没有任务那么让当前线程就先睡一会等待呼醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
temp = task.poll();//移除并返回队列头部元素,如果队列头部为空,则返回null
}
if(temp!=null){
temp.run();
}
}
}
};
tpl.add(ruMain);
Thread th = new Thread(ruMain);
th.setName("("+i+")");
th.start();//用一个线程来驱动这个任务体
}
}
public static void main(String[] args) {
HXMThreadPools ntp = new HXMThreadPools(100);//初始化三个线程
for(int z=0;z<10000;z++){
final int y = z;
ntp.addTask(new Runnable(){
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread()+"正在接管--我是任务:"+y);
}
});
}
}
}
图文解说
时间:2017年3月15日文思海辉信息技术有限公司
自JDK1.5问世以来JDK的开发者在线程处理方案中提供了很多多线程方面的工具包在java.util.current包下,尤其显著的就是线程池的出现,也算是JDK的一个
里程碑版本;
这里我们仅仅讨论线程池的功能和设计原理我们可以先回到没有线程池的年代,业务开发中出现的一些性能和障碍瓶颈问题?比如在使用Java写网络
服务的时候,通常在服务端会使用一个SocketServer来监听网卡上指定的一个端口(通常端口选择大于1024小于65535,1024之内的端口基本被操作系统
内部分配完毕),客户端根据主机地址和服务进程监听的端口号来与服务进行网络上的通信,在并发量比较小的情况下,通常的设计方案即是客户端发来一
个请求,服务端与之建立一个相应的线程来处理和该客户端之间的数据业务处理,业务处理完毕之后,由于Jvm有自动垃圾回收策略,所以程序员不必自己
考虑线程的回收,无效的线程会被JVM自行回收(一定程度上减少了程序员的工作量,其核心的思想是减轻JVM自身的"重量");可是如果在并发量比较大
的情况下,比如淘宝的双十一活动,在不考虑集群服务的情况下(其实集群也是利用多台服务来分担请求压力), 单点来处理的话这时将会出现灾难,严重
将会导致服务器宕机,那么出现这样的问题是什么原因造成的呢?通常情况下每款操作系统中对每一个进程内部的线程数量是有一定限制的,因为当线程
无限制多的情况下,操作系统(这里先考虑只是在单核的情况下)内部的调度算法去不停的切换线程赋予执行权,这将会导致有些线程会在很长时间内得不到
执行权,而且每一个线程的处理数据都需要单独的开辟一个工作空间,并且操作系统也要对这些线程的处理流程进行监控和记录,可想而知在线程无限多
的情况下,操作系统也不能给予很好的解决,最终出现的问题可能是死机,或者卡死现象,严重将会导致服务关闭;
线程池的出现是是在一定的程度上解决了这些问题,它的设计思想是根据业务的规则,可以先前制定好一个线程池,也就是一定数量的线程(当然这个
数量的线程数是操作系统可以接纳的),当有任务请求而来的时候先放入一个有序的队列中,然后线程池中的线程会去以争抢的方式(这里的争抢其实是无序
的且没有规则的,完全由运行的平台操作系统调度算法来控制的)去把消费任务队列中的任务(这里消费是一个比较关键的过程,)取出进行消
费;这样的处理方式优点是操作系统的压力得以减轻,宕机的现象不会出现;缺点就是这些线程池中的线程去从队列中取出任务时,由于这个任务队列被
所有线程所共享,所以必定会有一个加锁和释放锁的过程,从而导致很多线程得到了CPU赋予的执行权却没有操作业务的执行权,在一定程度上加剧了整
个响应过程的耗时,当然这相比系统宕机问题必然是一个更好的选择;
这里我贴上自己手写的线程池代码;
package com.pactera.com;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class HXMThreadPools {
private int initNum;//初始化线程数量
private List<Runnable> tpl = Collections.synchronizedList(new ArrayList<Runnable>());//创建一个线程池
private LinkedBlockingQueue<Runnable> task = new LinkedBlockingQueue<Runnable>();//任务队列
public HXMThreadPools(int initNum){
this.initNum = initNum;
this.initTPL();
}
public void addTask(Runnable taskTemp){//增加任务
synchronized(task){
this.task.add(taskTemp);
this.task.notifyAll();//唤醒哪些因访问任务队列没有得到任务而导致睡眠的线程兄弟们
}
}
private void initTPL(){
for(int i=0;i<this.initNum;i++){
Runnable ruMain = new Runnable(){
@Override
public void run() {
while(true){//让线程处于永痕的工作状态
Runnable temp = null;
synchronized(task){
while(task.isEmpty()){//如果当前任务列表中无任务
try {
task.wait();//如果没有任务那么让当前线程就先睡一会等待呼醒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
temp = task.poll();//移除并返回队列头部元素,如果队列头部为空,则返回null
}
if(temp!=null){
temp.run();
}
}
}
};
tpl.add(ruMain);
Thread th = new Thread(ruMain);
th.setName("("+i+")");
th.start();//用一个线程来驱动这个任务体
}
}
public static void main(String[] args) {
HXMThreadPools ntp = new HXMThreadPools(100);//初始化三个线程
for(int z=0;z<10000;z++){
final int y = z;
ntp.addTask(new Runnable(){
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread()+"正在接管--我是任务:"+y);
}
});
}
}
}
图文解说
时间:2017年3月15日文思海辉信息技术有限公司
相关文章推荐
- java线程池---编写自己的线程池
- java线程池--自己编写的线程池,非Eclipse自带
- 3.第三单元任务二实训:使用作业提交系统提交Java作业时 ,需要输入Java源代码文件名和自己的邮箱,提交前对Java文件名以及邮箱进行有效检查。编写程序实现对输入的Java源文件名以及邮箱有效性的
- 利用Java编写简单的WebService实例-转载
- java编写Producer(线程池,kafka)
- java编写Producer(线程池,kafka)
- java编写Producer(线程池,kafka)
- 利用Java 编写手机应用程序 Motorola iDEN篇
- java利用qrcode生成带有logo的二维码(logo位置及大小自己调)
- 【ASP.NET专题】(4)——利用ASP.NET MVC编写自己想要的页面
- java线程池工具类代码(利用java官方线程池类ExecutorService实现)
- 利用Java编写简单IIS日志清理工具
- Java之编写一个自己的加载器
- 统计文本文件中单词出现频率,自己编写的Java小程序
- 编写一个Java应用程序,直接查询自己主机的IP地址和Internet上的某个www服务器地址
- 利用java编写网络通信程序
- 利用eclipse编写高质量的java代码
- 利用Java编写简单的WebService实例
- [置顶] android利用jni调用第三方库——第三篇——编写库android程序整合第三方库libhello.so到自己的库libhelloword.so