[改善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并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...
随机推荐
- HDU 4893 Wow! Such Sequence! (线段树)
Wow! Such Sequence! 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4893 Description Recently, Doge ...
- How to include JavaScript file in JSF
In JSF 2.0, you can use <h:outputScript /> tag to render a HTML "script" element, an ...
- Spring init-method and destroy-method example
In Spring, you can use init-method and destroy-method as attribute in bean configuration file for be ...
- java assert的用法简介
assert的基本用法 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库 ...
- Linux后台运行程序
Linux后台运行程序 最近写的程序需要部署到Linux服务器上,按照以前的方式,在运行后面增加&,程序会切换为后台运行.但因为Linux一般是通过ssh远程登录的,等到退出当前session ...
- CodeForces 706B Interesting drink (二分查找)
题意:给定 n 个数,然后有 m 个询问,每个询问一个数,问你小于等于这个数的数有多少个. 析:其实很简单么,先排序,然后十分查找,so easy. 代码如下: #pragma comment(lin ...
- poj 3635 Full Tank? ( bfs+dp思想 )
Full Tank? Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5857 Accepted: 1920 Descri ...
- SQL中DATE和DATETIME类型不能直接作比较
如题,今天纠结了一天的问题. 在存储过程中定义了两个datetime类型的时间,然后把这个两个时间作为where条件中一个date字段between的两个时间段,结果无论如何都不执行... 就像 u ...
- MX记录查询
nslookup set type=mx
- “WinMount”和“云端”真是相当好用!
WinMount作为一款压缩文件管理以及虚拟光驱工具已经无敌了.更有两项功能相当好用: 1.将rar.zip等压缩文件直接虚拟成磁盘,也就是下载一个7G的游戏可以不用解压直接安装了! 2.右键压缩文件 ...