什么是线程池

  学习编程的小伙伴们会经常听到“线程池”、“连接池”这类的词语,可是到底“池”是什么意思呢?我讲个故事大家就理解了:在很久很久以前有一家银行,一年之中只有一个客户来办理业务,随着时间的推移,办理业务的人数每年都增加五千。20年之后这家银行办理业务的人次已经到十万。最开始只有一个客户的时候银行只需要雇佣一个按办理业务次数计工资的临时工就行了,办完业务就解雇。随着办理业务的人不断增多,银行老板发现继续雇佣按次计费的员工太麻烦了,每天都在招人,又每天都解雇人。所以老板就想出了一个办法,雇佣几个员工一直在办事大厅待命,有顾客来的时候,柜员就给顾客办业务,办完一个之后再继续为下一个顾客办业务,如果没有下一个顾客就继续待命。就这样这个“聪明”的老板发明了“柜员池”。

线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。

Java中使用线程池

线程池的种类

  Java中有三个比较常用的线程池,分别是FixedThreadPool,SingleThreadExecutor,CachedThreadPool。

1)   FixedThreadPool

  这是一个线程数固定的线程池,当这个线程池被创建的时候,池里的线程数就已经固定了。当需要运行的线程数量大体上变化不大时,适合使用这种线程池。固定数量还有一个好处,它可以一次性支付高昂的创建线程的开销,之后再使用的时候就不再需要这种开销。

2)   SingleThreadExecutor

  这是一个线程数量为1的线程池,所有提交的这个线程池的任务都会按照提交的先后顺序排队执行。单个线程执行有个好处:由于任务之间没有并发执行,因此提交到线程池种的任务之间不会相互干扰。程序执行的结果更具有确定性。

3)   CachedThreadPool

  一看到Cache就知道这是一个和缓存有关的线程池,每次有任务提交到线程池的时候,如果池中没有空闲的线程,线程池就会为这个任务创建一个线程,如果有空闲的线程,就会使用已有的空闲线程执行任务。有的人可能会有个疑惑:这样线程不就越来越多了吗?其实不是的,这个线程池还有一个销毁机制,如果一个线程60秒之内没有被使用过,这个线程就会被销毁,这样就节省了很多资源。CachedThreadPool是一个比较通用的线程池,它在多数情况下都能表现出优良的性能。以后编码的时候,遇事不决,用缓存(线程池)。

线程池的使用

  上面说了半天也没说这东西到底咋用,有的小伙伴已经等不及了。其实Java的线程池使用起来很简单。只需要一个接口和一个类就行:

ExecutorService是所有的线程池都实现了的接口,用这个接口可以定义句柄。

Executors类用于创建不同种类的线程池,Executors.new***()方法就可以返回一个线程池的对象,***表示线程池的名字。

具体代码如下:

public class CreateThreadPool{
public static void main(String[] args) {
//定义一个有5个线程的线程池
ExecutorService fixedTP = Executors.newFixedThreadPool(5);
ExecutorService singleTE = Executors.newSingleThreadExecutor();
ExecutorService cachedTP = Executors.newCachedThreadPool();
}
}

  不同的线程池之间只有定义的方式略有区别,使用方法是完全相同的。下面以缓存线程池为例来介绍线程池的使用方法:

class ExampleThread implements Runnable {
@Override
public void run() {
System.out.println("Do something");
}
}
public class UseThreadPool {
public static void main(String[] args) {
ExecutorService cachedTP = Executors.newCachedThreadPool();
cachedTP.execute(new ExampleThread());
cachedTP.shutdown();
}
}

  ExampleThread实现了Runnable接口,这次我们没有使用Thread类创建线程,而是使用的线程池,直接调用线程池的execute()方法并将ExampleThread的对象传进去就可以创建线程了,execute()方法可以接收任何实现Runnable接口的类的对象。使用线程池的时候别忘了调用shutdown()方法关闭线程池。继承自Thread类的对象也可以用同样的方式创建线程,因为Thread类也实现了Runnable接口。

  上次我们还讲到了可以通过实现Callable接口创建线程,这个线程就不能使用execute()方法了。线程池还有另一个方法:submit()方法接收实现Callable接口的的对象,同时还有一个重载的方法,重载方法接收实现Runnable方法的类。也就是说不管是实现了Runnable还是Callable的类都可以作为它的参数。submit()方法还有一个Future类型的返回值,Future用于获取线程的返回值,Future是一个有泛型的类,泛型的类型与Callable的泛型相同。演示代码如下:

class ExampleThread implements Callable<String> {
@Override
public String call() {
return "Some Value";
}
}
public class UseThreadPool {
public static void main(String[] args) {
ExecutorService cachedTP = Executors.newCachedThreadPool();
Future<String> future = cachedTP.submit(new ExampleThread());
try {
String value = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
cachedTP.shutdownNow();
}
}

  future.get()方法,这个方法会阻塞当前线程,一直等到线程池里相应的线程执行结束的时候当前线程才会解除阻塞。因此,这个get()方法也是只有等到不得不用返回值的时候才会调用,否则会影响程序运行的效率。

  shutdown()方法还有一个孪生兄弟shutdownNow(),两者区别如下:

shutdown():调用这个方法之后就线程池就不会再接收其它任务了。

shutdownNow():调用这个方法会向所有的线程发一个中断信号(如果线程选择忽略,这个线程还是会继续执行),并且不再执行排队中的任务,将排队中的任务作为返回值返回(List<Runnable>)。

总结

  线程池是一个典型的“用空间换时间”的应用案例,在线程池中始终维护一定数量的线程,这样不必每次都创建新的线程,代价是线程即使空闲的时候也要占用内存资源。当需要频繁创建和销毁线程的时候,使用线程池可以显著提高系统的运行效率,在线程池的不同种类中,缓存线程池在通常情况下都是性能最好的。在提交任务到线程池的时候,通常实现Runnable接口的使用execute()方法提交,实现Callable接口的使用submit()方法提交。最后,使用线程池后别忘了shutdown()。公众号:今日说码。关注我的公众号,可查看连载文章。遇到不理解的问题,直接在公众号留言即可。

Java并发编程(三)什么是线程池的更多相关文章

  1. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  2. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  3. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors

    [Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...

  4. [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...

    [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ... 摘要 介绍 Java 并发包里的几个主要 ExecutorService . 正文 ...

  5. Java并发编程(08):Executor线程池框架

    本文源码:GitHub·点这里 || GitEE·点这里 一.Executor框架简介 1.基础简介 Executor系统中,将线程任务提交和任务执行进行了解耦的设计,Executor有各种功能强大的 ...

  6. Java并发编程(十一)线程池的使用

    1.new Thread的弊端如下: a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom. c. 缺乏更多 ...

  7. Java并发编程之深入理解线程池原理及实现

    Java线程池在实际的应用开发中十分广泛.虽然Java1.5之后在JUC包中提供了内置线程池可以拿来就用,但是这之前仍有许多老的应用和系统是需要程序员自己开发的.因此,基于线程池的需求背景.技术要求了 ...

  8. Java并发编程、多线程、线程池…

    <实战java高并发程序设计>源码整理https://github.com/petercao/concurrent-programming/blob/master/README.md Ja ...

  9. java并发编程(四) 线程池 & 任务执行、终止源码分析

    参考文档 线程池任务执行全过程:https://blog.csdn.net/wojiaolinaaa/article/details/51345789 线程池中断:https://www.cnblog ...

  10. Java并发编程:4种线程池和缓冲队列BlockingQueue

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...

随机推荐

  1. javascript 获取服务时间

    用到了jquery的ajax方法,ajax自己写也可以. 具体用法 var setId = setInterval(function(){ var xhr = $.ajax({ type: 'HEAD ...

  2. WinSxS文件夹瘦身

    WinSxS文件夹瘦身 2014-5-8 18:03:32来源:IT之家作者:阿象责编:阿象 评论:27 刚刚,我们分享了如何用DISM管理工具查看Win8.1 WinSxS文件夹实际大小.对于Win ...

  3. HDFS原理解析

    一.HDFS简介 HDFS为了做到可靠性(reliability)创建了多分数据块(data blocks)的复制(replicas),并将它们放置在服务器群的计算节点中(computer nodes ...

  4. php下载远程大文件(获取远程文件大小)

    function download_file($url) { // $url = http://192.168.8.95/vm/download_file?downurl=http://192.168 ...

  5. 8.Bootstrap CSS编码规范

    Bootstrap CSS编码规范 本节的介绍内容为 Bootstrap 中的 CSS 编码规范. 语法 用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展现的方法. 为选择 ...

  6. 在C#中internal、protected internal关键字是什么意思?

    internal:就是程序集. 那么什么是程序集呢?就是“项目”也就是工程里中的csproj 比如:我有个解决方案,这个方案中有2个项目,1个是控制台程序,1个是webapi项目,那么我这个解决方案中 ...

  7. oralce的判断语句

    大家对 IF ELSE 语句应该都很熟悉吧,它是用来对过程进行控制的.在 SQL 的世界中 CASE 语句有类似的效果.下面简单的介绍 CASE 语句的用法. CASE 语句的形式 事实上,CASE ...

  8. python之复数

    #coding=utf8 ''''' 复数是由一个实数和一个虚数组合构成,表示为:x+yj 一个负数时一对有序浮点数(x,y),其中x是实数部分,y是虚数部分. Python语言中有关负数的概念: 1 ...

  9. 深入浅出SharePoint2010——请假系统无代码篇之权限设计

    首选我们需要区分3个跟权限相关的概念. 权限项目(Permission):最小的权限粒度.比如创建列表项.审批等. 权限级别(Permission Level):权限项目不能直接赋予用户或者用户组,只 ...

  10. 在python中逐行读取大文件

    在我们日常工作中,难免会有处理日志文件的时候,当文件小的时候,基本不用当心什么,直接用file.read()或readlines()就可以了,但是如果是将一个10G大小的日志文件读取,即文件大于内存的 ...