[改善Java代码]优先选择线程池
在Java1.5之前,实现多线程编程比较麻烦,需要自己启动线程,并关注同步资源,防止线程死锁等问题,在1.5版本之后引入了并行计算框架,大大简化了多线程开发.
我们知道线程有5个状态:新建状态(New),可运行状态(Runnable,也叫做运行状态),阻塞状态(Blocked),等待状态(Waiting),结束状态(Terminated),线程的状态只能由新建状态转变为运行状态后才可能被阻塞或者等待,最终终结.
不可能出现本末倒置的情况,比如想把一个结束状态的线程转变为新建状态,则会出现异常.例如下面代码会出现异常:
import java.util.concurrent.TimeUnit;
public class Client {
public static void main(String[] args) throws Exception {
// 创建一个线程,新建状态
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("线程在运行……");
}
});
// 运行状态
t.start();
// 是否是运行态,若不是则等待10毫秒
while (!t.getState().equals(Thread.State.TERMINATED)) {
TimeUnit.MILLISECONDS.sleep(10);
}
System.out.println(t.getState());//TERMINATED
// 直接由结束态转变为运行态
t.start();
}
}
会报如下错误:
线程在运行……
TERMINATED
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Unknown Source)
at cn.summerchill.test.Client.main(Client.java:20)
出现上面的异常就是不能从结束状态直接转变成可运行状态.
一个线程的运行时间分为三部分:T1为线程的启动时间,T2为线程的运行时间,T3为线程的销毁时间.如果一个线程不能被重复使用,每次创建一个线程都需要经过启动,运行,销毁这三个过程,那么这势必会增大系统的响应时间,有没有一个更好的方法降低线程的运行时间?
T2是无法避免的,只有通过代码优化缩短线程的运行时间.
T1和T2都可以通过线程池ThreadPool来缩短时间,比如在容器或者系统启动时,创建足够多的线程,当容器或系统需要时直接从线程池中获取,运算出结果再把线程返回到线程池中---ExecutorService就是实现了线程池的执行器,看实例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; public class Client {
public static void main(String[] args) {
//2个线程的线程池
ThreadPoolExecutor es = (ThreadPoolExecutor)Executors.newFixedThreadPool(2);
//多次执行线程体
for (int i = 0; i < 4; i++) { es.submit(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
System.out.println(es.getPoolSize());
System.out.println(es.getCorePoolSize());
//关闭执行器
es.shutdown();
}
}
运行结果:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-2
2
2
pool-1-thread-1
本次代码执行了4遍线程体,按照我们之前说的:一个线程不可能从结束状态转变为可运行状态,那为什么此处的2个线程可以反复使用呢?这就是我们要搞清楚的重点:
线程池的实现涉及以下三个名词:
(1)工作线程(Worker)
线程池中的线程,只有两个状态:可运行状态和等待状态,在没有任务的时候就处于等待状态,运行时可以循环的执行任务.
(2)任务接口(Task)
这是每个任务必须实现的接口,以供工作线程调度器调度,它主要规定了任务的入口,任务执行完的场景处理,任务的执行状态等...
这里有两种类型的任务,具有返回值或异常的Callable接口任务和无返回值并兼容旧版本的Runnable接口任务.
(3)任务队列(Work Queue)
用于存放等待处理的任务,一般是BlockingQueue的实现类,用来实现任务的排队处理.
***********分析源码****************
线程池的创建过程:
创建一个阻塞队列以容纳任务,在第一次执行任务时创建足够多的线程(不可超过许可线程数),并处理任务.之后每个工作线程自行从任务队列中获得任务,直到任务队列中的任务数量为0为止.
此时线程处于等待状态,一旦有任务再加入到队列中,即唤醒工作线程进行处理,实现线程的可复用性.
使用线程池减少的是线程的创建和销毁时间,这对于多线程来说非常有帮助.
我们常用的Servlet容器,每次请求处理的都是一个线程,如果不采用线程池技术,每次请求都会重新创建一个线程,这会导致系统的性能负荷加大,响应效率下降,降低了系统的友好性.
[改善Java代码]优先选择线程池的更多相关文章
- [改善Java代码]适时选择不同的线程池来实现
Java的线程池实现从最根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系,但是Java为了简化并行计算,还提供 ...
- [改善Java代码]优先使用整型池
建议28: 优先使用整型池 看如下代码: public class Client { public static void main(String[] args) { Scanner input = ...
- [改善Java代码]适时选择getDeclaredxxx和getxxx
Java的Class类提供了很多的getDeclaredxxx方法和getxxx方法,例如getDeclaredmethod和getMethod成对出现,getDeclaredConstructors ...
- [改善Java代码]自由选择字符串拼接方法
对一个字符串拼接有三种方法:加号,contact方法,StringBuffer或者StringBuilder的append方法,其中加号是最常用的.其他两种方式偶尔会出现在一些开源项目中,那么这三者有 ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- JAVA基础拾遗-论线程池的线程粒度划分与深浅放置
摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- 深入理解Java自带的线程池和缓冲队列
前言 线程池是什么 线程池的概念是初始化线程池时在池中创建空闲的线程,一但有工作任务,可直接使用线程池中的线程进行执行工作任务,任务执行完成后又返回线程池中成为空闲线程.使用线程池可以减少线程的创建和 ...
- Java并发编程:线程池的使用(转载)
转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
随机推荐
- 设置结点的ID为固定ID
https://www.java.net//forum/topic/jxta/jxta-community-forum/how-initialize-pse-jxse-27 ————————————— ...
- Light oj 1197 - Help Hanzo (素数筛技巧)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1197 给你a和b求a到b之间的素数个数. 先在小区间素数筛,大区间就用类似素数筛的想法 ...
- window.parent != window 解决界面嵌套问题
页面在被嵌套的时,效果:,,如果用户点击“刷新”,该问题即可解决. 如果想通过代码解决的话,这个问题属于客户端的问题,不是服务器端的问题. 如果直接写:window.location.href = “ ...
- 实现CCLayer只显示一个矩形可见区域
转自:http://blog.csdn.net/while0/article/details/11004147 CCLayer的区域可能会比较大,怎样让它只显示其中一部分区域呢? 这个还是有很多场景 ...
- skyline TerraBuilder 制作MPT方法与技巧(2)
制作MPT的方法可以看这里<skyline TerraBuilder 制作MPT方法与技巧(1)>http://www.cnblogs.com/cannel/p/3622447.html ...
- C++ 对象没有显式初始化
C++ 对象没有显式初始化,结果是什么? 首先考虑非静态对象 1.方法内的局部对象: a.类类型:调用default构造方法 b.基本类型:值不确定 2.类中的数据成员: a.类类型:调用defaul ...
- SAP CRM 最新简介文字(2007年、中英文)
以下内容是SAP CRM功能的精简描述,摘自SAP官方文档,附上中英文版本,可以对SAP CRM的主要功能有大致了解. 营销 - 使用营销资源管理.客户细分及列表管理.营销活动管理.线索管理.贸易促销 ...
- iOS开发——UI_swift篇&TableView实现页眉和页脚
TableView实现页眉和页脚 在UItableView中header和footer是很常见的,而且他能让你实现很复杂的功能,我们见过最多的就是下拉刷新和上啦加载更多,当然你还可以在上面添加一个 ...
- php实现网页标签补全方法(转)
导读:PHP在生成静态文件的时候,有时候会因为一些混编问题让HTML标签不完整或混乱而导致页面混乱.作者分享下面这段小代码可以非常方便解决问题. 如果你的网页内容的html标签显示不全,有些表格标签不 ...
- ABAP 日期时间函数(转)
转自:http://www.sapjx.com/abap-datetime-function.html 函数名称 (内页-点击名称可查看操作) 函数说明 备注 FIMA_DATE_CREATE RP_ ...