在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代码]优先选择线程池的更多相关文章

  1. [改善Java代码]适时选择不同的线程池来实现

    Java的线程池实现从最根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系,但是Java为了简化并行计算,还提供 ...

  2. [改善Java代码]优先使用整型池

    建议28: 优先使用整型池 看如下代码: public class Client { public static void main(String[] args) { Scanner input = ...

  3. [改善Java代码]适时选择getDeclaredxxx和getxxx

    Java的Class类提供了很多的getDeclaredxxx方法和getxxx方法,例如getDeclaredmethod和getMethod成对出现,getDeclaredConstructors ...

  4. [改善Java代码]自由选择字符串拼接方法

    对一个字符串拼接有三种方法:加号,contact方法,StringBuffer或者StringBuilder的append方法,其中加号是最常用的.其他两种方式偶尔会出现在一些开源项目中,那么这三者有 ...

  5. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  6. JAVA基础拾遗-论线程池的线程粒度划分与深浅放置

    摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...

  7. Java并发编程:线程池的使用(转)

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

  8. 深入理解Java自带的线程池和缓冲队列

    前言 线程池是什么 线程池的概念是初始化线程池时在池中创建空闲的线程,一但有工作任务,可直接使用线程池中的线程进行执行工作任务,任务执行完成后又返回线程池中成为空闲线程.使用线程池可以减少线程的创建和 ...

  9. Java并发编程:线程池的使用(转载)

    转载自:https://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

随机推荐

  1. SAE 合并图片

    $domain = 'picleader'; //图片库的域名 $stgurl = 'http://lemonluoxing-picleader.stor.sinaapp.com/'; //绝对路径 ...

  2. http://www.hameister.org/JavaFX_PuzzleGame.html

    http://www.hameister.org/JavaFX_PuzzleGame.html

  3. Xshell异常断开

    这可能是由于 SSH 超时断开连接 导致的!可以这样做...修改/etc/ssh/sshd_config文件,找到 ClientAliveInterval 0和ClientAliveCountMax ...

  4. thymeleaf中的Literals

    Literals即为文字 一.Text literals:文本文字 文本文字只是字符串指定的单引号之间.他们可以包含任何字符,但你应避免任何单引号里面\ ' <p> Now you are ...

  5. UVaLive 7375 Hilbert Sort (递归,四分图,模拟)

    题意:告诉你一条希尔伯特曲线的大小,然后给你n 个人,及n 个人的坐标,你的起点是左下角,终点是右下角,按照希尔伯特的曲线去走,按照这个顺序给n个人排序, 按顺序输出每个人的名字! 析:这就是一个四分 ...

  6. CodeForces 589A Email Aliases (匹配,水题)

    题意:给定于所有的邮箱,都是由login@domain这样的形式构成,而且字符都是不区分大小写的. 我们有一种特殊类型的邮箱——@bmail.com, 这种邮箱除了不区分大小写外—— 1,'@'之前的 ...

  7. matlab中读取txt数据文件(转)

    根据txt文档不同种类介绍不同的读取数据方法 一.纯数据文件(没有字母和中文,纯数字) 对于这种txt文档,从matalb中读取就简单多了 例如test.txt文件,内容为“17.901 -1.111 ...

  8. kaptcha验证码插件的使用

    kaptcha 是一个非常实用的验证码生成工具.有了它,你可以生成各种样式的验证码,因为它是可配置的.kaptcha工作的原理是调用 com.google.code.kaptcha.servlet.K ...

  9. Navicat for mysql 11.0破解方法

    Navicat for mysql破解器 首先下载破解器,然后解压至随意一个目录下.最后双击exe程序,按照提示找到你安装好的navicat for mysql文件夹下的navicat.exe程序,之 ...

  10. javascript 笔记(待续)

    1.基础对象  var o=new Object();  o.xxx=1; o.xx=2;    var 01={xxx=1,xx=2} 2.==与===   "5"==5 Tru ...