【java线程系列】java线程系列之java线程池详解
一线程池的概念及为何需要线程池:
我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在我们的程序中需要频繁使用线程,且每个线程执行的时间很短,短到几乎小于线程创建及销毁的时间那么代价将会更大,如:服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。显然如果频繁的创建销毁线程效率将非常低。
那么我们能否让一个线程可以复用,即当一个线程执行完后不销毁该线程,而是让其等待执行其它的任务.答案就是使用线程池。
何谓线程池:池线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
合理的使用线程池相对于单独使用线程的好处如下:
1 降低资源消耗,因为线程池减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2 提高响应速度。因为线程池中的线程的创建是线程池管理器来创建的,当任务到达时,任务可以不需要等到线程创建就能立即执行。
3提高线程的可管理性,线程池为线程生命周期开销问题和资源不足问题提供了解决方案,使用线程池可以对线程进行统一的分配,调优和监控。
二关于java线程池的几个核心类
说到线程池首先我们得了解三个类:Executors ,ExecutorService与ThreadPoolExecutor。其中Executors 相当于一个创建线程池的工具类,而ExecutorService才是真正的线程池接口,而ThreadPoolExecutor是ExecutorService的具体实现类。我们一个一个来介绍:
1Executors:创建线程池的工具类,在该类中提供了许多静态方法来创建一个线程池,该类中的重要方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
我们一个一个来看:
1 public static ExecutorService newFixedThreadPool(int nThreads)
通过传入的int类型整数创建一个固定大小的线程池(Creates a thread pool that reuses a fixed number of threads),每次提交一个任务就创建一个线程,当线程达到线程池的最大大小后提交的线程会在队列中等待。
2 public static ExecutorService newSingleThreadExecutor()
创建一个单线程的线程池(Creates an Executor that uses a single worker thread)。
3 public static ExecutorService newCachedThreadPool()
创建一个可以缓存的线程池,具体思想是当无线程空闲时创建一个新线程,否则重用先前创建的线程当先前创建的线程空闲时(Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.)
4 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个可以定时执行或周期性执行的线程池(Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.)
2ExecutorService:可以看到在上述介绍的Executors的几个重要方法的返回值均为ExecutorService,我们先来看一下其类的定义:
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以看到ExecutorService是一个接口它继承自Executor(注意此处不是上面介绍的Executors),那我们来看一下Executor接口的定义:
public interface Executor {
void execute(Runnable command);
}
可以看到Executor接口代码非常简单仅仅包含一个void execute(Runnable command);方法的声明而已。也就是说ExecutorService接口继承自Executor接口,然后在此基础上添加了一些自己的方法。
3ThreadPoolExecutor类:这个是创建一个线程的核心类,也是我们讲解的重点,首先我们来看一下其类的定义:
public class ThreadPoolExecutor extends AbstractExecutorService
可以看到ThreadPoolExecutor类继承自AbstractExecutorService,那么我们来看一下AbstractExecutorService类的定义:
public abstract class AbstractExecutorService implements ExecutorService
可以看到AbstractExecutorService类是一个抽象类,它实现了ExecutorService,至于AbstractExecutorService类的内容,比较多我就不贴出来了,感兴趣的可以去看一下源码,读者只需要知道AbstractExecutorService类它实现了 ExecutorService接口中的绝大部分方法,部分方法未实现因此它是一个抽象类。
接下来看一下ThreadPoolExecutor类中的构造器。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
可以看到ThreadPoolExecutor类为我们提供了四个构造器,其中第四个是最基本的构造器,其余三个构造器均在其方法内调用了第四个构造器。所以我们重点讲解第四个构造器的各参数的意义:
1 int corePoolSize:内核池的大小,是一个int型的参数,
2 int maximumPoolSize:线程池的最大线程数,是一个int型的参数,它表示在线程池中最多能创建多少个线程(the maximum number of threads to allow in the pool)
3 long keepAliveTime:表示无任务执行时线程最多维持多久后终止,是一个long类型的参数(this is the maximum time that excess idle threads will wait for new tasks before terminating.),只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用(when the number of threads is greater than the core),直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。
4 TimeUnit unit:参数keepAliveTime的时间单位(the time unit for the {@code keepAliveTime} argument)
5 BlockingQueue<Runnable> workQueue:阻塞队列,用来存储等待执行的任务(the queue to use for holding tasks before they are executed),这个队列仅仅容纳通过execute方法提交的Runnable接口的任务(the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable}
tasks submitted by the {@code execute} method.)
6 ThreadFactory threadFactory:线程工厂,主要用来创建线程(the factory to use when the executor creates a new thread)
7 RejectedExecutionHandler handler:它表示当一个线程的执行因为到达现场边界且队列容量达到极值而阻塞时(to use when execution is blocked because the thread bounds and queue capacities are reached)而拒绝执行任务( RejectedExecution)时应该采取的策略。它的取值是一个 RejectedExecutionHandler 接口,取值供四种情 况:
ThreadPoolExecutor.AbortPolicy:丢弃任务且抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
其中上述四个类都是ThreadPoolExecutor的静态内部类,它们均实现了 RejectedExecutionHandler 接口,其类的定义如下:
public static class AbortPolicy implements RejectedExecutionHandler public static class DiscardPolicy implements RejectedExecutionHandler public static class DiscardOldestPolicy implements RejectedExecutionHandler public static class CallerRunsPolicy implements RejectedExecutionHandler
接下来看一下ThreadPoolExecutor类的重要方法:
public void execute(Runnable command)
public void shutdown()
public List<Runnable> shutdownNow()
submit()
正如我们在上述介绍的,Executor接口仅仅包含一个void execute(Runnable command);方法的声明,ExecutorService接口继承自Executor接口,然后在此基础上添加了一些自己的方法。而AbstractExecutorService类它实现了ExecutorService接口中的绝大部分方法,少部分方法未实现(因此它是一个抽象类),而上述的几个方法中的execute(),shutdown(),shutdownNow()在AbstractExecutorService类中未实现,它们是在ThreadPoolExecutor类中实现的,而submit是在AbstractExecutorService类中实现的。
注意execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交给线程池去执行。
三java线程池的使用:
使用线程池时我们通常不是使用ThreadPoolExecutor这个核心类,而是使用Executors这个工具类中的几个静态方法,
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
Executors.newFixedThreadPool(int); //创建固定大小的线程池
Executors.newScheduledThreadPool(int) //创建一个定时执行的线程池
这几个方法的使用差不多,所以我们以创建固定大小的线程池Executors.newFixedThreadPool(int);这个方法为例来讲解线程池的使用,我打算以服务器端使用线程池来连接客户端的socket请求通信为例来讲解其使用,代码如下:
public class Server {
private ExecutorService executorService;// 线程池
private ServerSocket serverSocket = null;
private Socket socket = null;
private boolean isStarted = true;
public Server() {
try {
// 创建线程池,池中具有(cpu个数*50)条线程
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50);
serverSocket = new ServerSocket(Constants.SERVER_PORT);
} catch (IOException e) {
e.printStackTrace();
quit();
}
}
public void start() {
System.out.println(MyDate.getDateCN() + " 服务器已启动...");
try {
while (isStarted) {//将服务器端的accept操作放在一个while循环中,用来不断监测客户端的连接请求
socket = serverSocket.accept();
String ip = socket.getInetAddress().toString();
System.out.println(MyDate.getDateCN() + " 用户:" + ip + " 已建立连接");
// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
//即将每个用户的请求单独放到一个线程中执行
if (socket.isConnected())
executorService.execute(new SocketTask(socket));// 添加到线程池
}
if (socket != null)
socket.close();
if (serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
// isStarted = false;
}
}
private final class SocketTask implements Runnable {
private Socket socket = null;
private InputThread in;
private OutputThread out;
private OutputThreadMap map;
public SocketTask(Socket socket) {
this.socket = socket;
map = OutputThreadMap.getInstance();
}
@Override
public void run() {
out = new OutputThread(socket, map);//
// 先实例化写消息线程,(把对应用户的写线程存入map缓存器中)
in = new InputThread(socket, out, map);// 再实例化读消息线程
out.setStart(true);
in.setStart(true);
in.start();
out.start();
}
}
/**
* 退出
*/
public void quit() {
try {
this.isStarted = false;
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server().start();
}
}
从上述代码示例可以看出,线程池的使用步骤如下:
1使用Executors这个工具类中的几个静态方法创建一个ThreadPoolExecutor对象,如
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50);// 创建线程池,池中具有(cpu个数*50)条线程
注意几个静态方法返回的是其父类ExecutorService接口,通常我们在指定线程的个数时不直接指定为一个固定值,而是使用类似Runtime.getRuntime.availableProcessors() * 50的方式充分利用多核计算机的性能,
2创建一个实现了Runnable接口的线程,如:
private final class SocketTask implements Runnable
重写其run方法,在run方法中完成自己的业务逻辑。
3调用ExecutorService对象的execute()方法执行2中创建的Runnable对象,该方法的参数是一个Runnable对象,如:
executorService.execute(new SocketTask(socket));// 添加到线程池
注意execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现。
好了以上就是本人理解的关于java线程池的内容,看官如果觉得不错,请记得点击下方的”顶“或赞给我一点鼓励哦!
【java线程系列】java线程系列之java线程池详解的更多相关文章
- 《手把手教你》系列基础篇(七十五)-java+ selenium自动化测试-框架设计基础-TestNG实现DDT - 中篇(详解教程)
1.简介 上一篇中介绍了DataProvider如何传递参数,以及和一些其他方法结合传递参数,今天宏哥接着把剩下的一些常用的也做一下简单的介绍和分享. 2.项目实战1 @DataProvider + ...
- 《手把手教你》系列基础篇(八十六)-java+ selenium自动化测试-框架设计基础-Log4j实现日志输出(详解教程)
1.简介 自动化测试中如何输出日志文件.任何软件,都会涉及到日志输出.所以,在测试人员报bug,特别是崩溃的bug,一般都要提供软件产品的日志文件.开发通过看日志文件,知道这个崩溃产生的原因,至少知道 ...
- Java线程池详解(二)
一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...
- 《手把手教你》系列技巧篇(七十一)-java+ selenium自动化测试-自定义类解决元素同步问题(详解教程)
1.简介 前面宏哥介绍了几种关于时间等待的方法,也提到了,在实际自动化测试脚本开发过程,百分之90的报错是和元素因为时间不同步而发生报错.本文介绍如何新建一个自定义的类库来解决这个元素同步问题.这样, ...
- 《手把手教你》系列基础篇(七十六)-java+ selenium自动化测试-框架设计基础-TestNG实现DDT - 下篇(详解教程)
1.简介 今天这一篇宏哥主要是结合实际工作中将遇到的测试场景和前边两篇学习的知识结合起来给大家讲解和分享一下,希望以后大家在以后遇到其他的测试场景也可以将自己的所学的知识应用到测试场景中. 2.测试场 ...
- 《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
1.简介 上一篇介绍了POM的基础理论知识和非POM方式写脚本,这篇介绍利用页面工厂类(page factory)去实现POM,通过查看PageFactory类,我们可以知道它是一个初始化一个页面实例 ...
- 《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
1.简介 上一篇宏哥用PageFactory实现了POM,宏哥再介绍一下如果不用PageFactory如何实现POM. 2.项目实战 在这里宏哥以百度首页登录的例子,如果用POM实现,在测试脚本中实际 ...
- 《手把手教你》系列基础篇(九十六)-java+ selenium自动化测试-框架之设计篇-跨浏览器(详解教程)
1.简介 从这一篇开始介绍和分享Java+Selenium+POM的简单自动化测试框架设计.第一个设计点,就是支持跨浏览器测试. 宏哥自己认为的支持跨浏览器测试就是:同一个测试用例,支持用不同浏览器去 ...
- Java集合中List,Set以及Map等集合体系详解
转载请注明出处:Java集合中List,Set以及Map等集合体系详解(史上最全) 概述: List , Set, Map都是接口,前两个继承至collection接口,Map为独立接口 Set下有H ...
- java 日志体系(三)log4j从入门到详解
java 日志体系(三)log4j从入门到详解 一.Log4j 简介 在应用程序中添加日志记录总的来说基于三个目的: 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作: 跟踪代 ...
随机推荐
- SAM维护的在线LCS
题目大意: 给定两个字符串,存在三种操作,分别是在a,b串末尾加一个字符串,和询问两串的LCS 题解: Get新套路:把两串建在同一SAM上,将重合的位置合并为同一节点,再加个标记数组,如果两者的LC ...
- hdu 5505(GT and numbers)
题意: 给你a和b,a每次和它的因子相乘得到一个新的a,求多少次后可以得到b. 输入样例 3 1 1 1 2 2 4 输出样例 0 -1 1 思路: 每次找出a和b/a的最大公约数(即当前a想得到b能 ...
- linux心得
cd .. 返回上一级文件夹cd /xxx/xxxx/xx 进入文件夹cd Desktop 进入桌面sudo vim /etc/vim/vimrc 进入vim配置器:w xxx 保存为名为xxx的文件 ...
- QSDK下驱动AR8035
0 概述 QSDK平台中,我所接触到的版本,能支持MIPS架构的,是基于Openwrt AA版本:虽然CC版本上就已经能很好地支持AR8035了,可是AA版本它本身是不支持的,于是不断有人要求提供补丁 ...
- Maven之自定义archetype生成项目骨架
Maven之自定义archetype生成项目骨架(一) http://blog.csdn.net/sxdtzhaoxinguo/article/details/46895013
- session.save()返回值问题
正常都应该返回插入的主键 但是 如果你用sessionFactory来写就一定返回0 先科普下持久化数据库的三个状态方便下面理解 一次会话状态中,持久化对象经历以下三种状态:1 transient:对 ...
- 46. Permutations(medium, backtrack, 重要)
Given a collection of distinct numbers, return all possible permutations. For example, [1,2,3] have ...
- vmware迁移到openstack的一些坑
title: 安全平台迁移 tags: 新建,模板,小书匠 grammar_cjkRuby: true --- 前言 主要有三个坑: 一是如果原先虚拟机没有安装virtio驱动,要设置设备驱动为ide ...
- 动手实现一个vue中的模态对话框组件
写在前面 对话框是很常用的组件 , 在很多地方都会用到,一般我们可以使用自带的alert来弹出对话框,但是假如是设计 出的图该怎么办呢 ,所以我们需要自己写一个对话框,并且如果有很多地方都用到,那我们 ...
- Elasticsearch+Hbase实现海量数据秒回查询
---------------------------------------------------------------------------------------------[版权申明:本 ...