什么是线程池

  学习编程的小伙伴们会经常听到“线程池”、“连接池”这类的词语,可是到底“池”是什么意思呢?我讲个故事大家就理解了:在很久很久以前有一家银行,一年之中只有一个客户来办理业务,随着时间的推移,办理业务的人数每年都增加五千。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. 基于Apache Curator框架的ZooKeeper使用详解

    一 简介 Apache Curator是一个比较完善的ZooKeeper客户端框架,通过封装的一套高级API 简化了ZooKeeper的操作.通过查看官方文档,可以发现Curator主要解决了三类问题 ...

  2. 状态开关(ToggleButton)

    状态开关(ToggleButton): 常用属性:isChecked(是否被选中,如true) 监听:1.监听方法:setOnCheckedChangeListener 2.监听器:CompoundB ...

  3. QQ群文件下载速度慢-解决办法

    QQ群文件下载速度慢-解决办法 本方法是本人亲测测试出来的,特此和大家分享 没有效果让你打我 解决方法 1.打开[手机版 QQ] 2.进入群文件,找到需要下载文件 3.[分享],先点击[发给好友],选 ...

  4. mac 下常用快捷键

    1.快速搜索某个类 双击thift 2.切换不同的类: ctrl+方向键 3.alt+command+B 进入到具体的子类 但是 ctrl+方向键一直切的是电脑上 桌面的切换.打开 系统偏好设置-快捷 ...

  5. 使用button的open-type="getUserInfo"引导用户进行授权

    https://blog.csdn.net/weixin_39602178/article/details/80295684 一. 前言 小程序官方文档,上面说明 > wx.getUserInf ...

  6. lua垃圾回收之空表

    故事背景: 自己手动手写的一个lua外部库luaopen_xxx,采用了tolua++1.0.93,编译后得到xxx.dll,当在luajit中require 'xxx'后是正常的,但如果运行环境换成 ...

  7. [EffectiveC++]item13:Use objects to manage resources(RAII)

    baidu百科 RAII 百科名片 RAII,也称为“资源获取就是初始化”,是c++等编程语言常用的管理资源.避免内存泄露的方法.它保证在任何情况下,使用对象时先构造对象,最后析构对象. 目录 RAI ...

  8. JSON与对象的序列化与反序列化

    一.利用JavaScriptSerializer 类 System.Web.Script.Serialization空间,位于System.Web.extensions.dll中. JavaScrip ...

  9. 如果要遍历除了for循环,你还知道什么?——JavaScript的各种遍历方式

    如果要遍历除了for循环,你还知道什么?——JavaScript的各种遍历方式 这是曾经面试中的一道题目,当时的我用现在很潮的话讲,整个人是懵比的,我呆呆的说了句,我好像只知道for循环啊.后来回过神 ...

  10. vs项目同步到gitee方法

    前提:vs配置过登录后 进入命令行后执行: git push --set-upstream origin master