- 浏览: 701370 次
- 性别:
- 来自: 北京
博客专栏
-
读金庸故事,品程序人生
浏览量:47681
文章分类
最新评论
-
hty881008:
LZ,你的json返回是怎么出来的,我的怎么是No messa ...
使用CXF暴露您的REST服务 -
jxFY:
赞
Apache的对象池化工具commons-pool -
wangyudong:
新版本的Wisdom RESTClient地址https:// ...
使用CXF暴露您的REST服务 -
wangyudong:
由CXF实现的微服务需要有比较好的工具去测试RESTful A ...
使用CXF暴露您的REST服务 -
spring_springdata:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
Maven3实战笔记01环境配置与使用入门
1. 线程池是啥子
一说到池子,大家都会想到数据库连接池那种对象池。本来嘛,现在倡导废物回收利用的节能环保绿色新社会嘛。其实线程池的初衷就是能将已经创建好了的线程对象重复利用。之前咱们说过对于一个分布式系统,离不开高并发、多线程的支持。那么无论是HTTP方式的,还是文件方式的,面对海量的客户端请求,作为服务端如果对于请求使用单线程阻塞方式显然是不可能的。JDK5之后为咱们提供了现成的线程池对象。我们用几个现成的JDK辅助类就能将线程对象池化。线程池实际上也是对象池的一种特例,对象池主要是池化那些创建比较费劲、耗费资源比较大的大对象,比如你的重量级UI组件,比如你的IO文件对象。当然了,如果您的企业不差钱,买得起优越的服务器,仅仅针对一个不到100并发场景的小场景,每个数据服务终端都是当时很顶级的服务器,50w的IBM服务器,那另当别论,您愿意怎么折腾就怎么折腾!别以为笔者在这里开玩笑,确实有这样的现象。某政府的电子政务系统,花的纳税人的钱,买的都是高档服务器,配置的集群环境。最后的使用量,并发量……唉,花纳税人的钱就是不痛不痒啊,不提了,那个面子工程……
2. 为什么用这玩意
言归正传,我们来看为什么要用线程池这个东西。上面我们提到了对象池,对象池的目的就是为了重复利用创建起来比较复杂的大对象的。那么线程呢?线程对象不大的话也要用池子管理它吗?实际上线程池适合于短时间运行的、并发量比较大的线程对象场景。就好比说你去快餐店吃东西,快餐店不可能为每一个顾客都布置一个桌子,一把椅子,固定就那么10几个座位。您买完了一个汉堡,一杯可乐,在座位上吧唧吧唧2口,喝口水,ok,擦擦嘴,抬屁股走人即可。从买餐到坐下吃饭,不到5分钟。因为这家快餐店是知名品牌,有很高的客户群体。没办法,对于每个客户而言就那么珍贵的5分钟啊。轻轻的您走了,正如您轻轻地来。线程池就是适用于以上这种场景,并发量非常巨大,但是每个访问的时间又是极短的。如果是每次访问需要的时间比较多的情况下就不适合这种场景,就适合建立新线程对象的场景。就好比说您现在关心吃饭的质量,吃饭喜欢细嚼慢咽,带着老婆,唱着歌,吃着火锅然后服务员突然跟您说:“对不起,先生,您time out了。”您怎么想。这种场景就适合大排档,所有客户都是露天桌椅,如果来了新客户就应该在外面搭个桌子(new一个新线程),招呼客户去外面吃大排档了。
如果线程对象请求的时间过长,那么很多新线程对象都在线程等待队列中等着。等待的队伍越来越长不说,客户端也一直处于等待状态,总有一个时间,客户端会没有耐心的。那么我们在开发中,大概在什么情况下使用线程池来维护线程对象呢。
3. 怎么用这玩意
讲了线程池是什么,又描述了线程池的使用场景,那么如何使用线程池呢。咱们先来看一个实例
package threadPool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestThreadPool { /** * @param args */ public static void main(String[] args) { // 创建线程池 ExecutorService exec = Executors.newFixedThreadPool(3); // 建立100个线程 for (int index = 0; index < 100; index++) { Runnable run = new Runnable() { public void run() { // 随机毫秒 long time = (long) (Math.random() * 500); System.out.println("Sleeping " + Thread.currentThread().getName() + ":" + time + "ms"); try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } }; //执行 exec.execute(run); } //关闭 exec.shutdown(); } }
以上程序先创建了一个线程池,Executors可以创建各式各样的线程池,这个我们后面再说,先不去管它,就知道先创建了个线程池即可。在构造参数里限定线程池的容量是3个,也就是说线程池允许的活动状态线程个数是3个(这家快餐馆也太小了,就给开了3个座位,不够看美女的啊~),在同一时间内,3个线程任务争抢CPU资源执行任务。这里每一个Runnable对象相当于一个要吃饭的人,他们必须有桌子才能吃饭,而线程池为他们提供了桌子、椅子,Runnable们有了桌子、椅子才能吃东西。其他为活动的线程都在线程队列中等待着。最后调用关闭线程池的方法shutdown();,如果不调用该方法那么线程池还可以继续运行线程对象的任务,如果调用了再执行线程对象会抛出java.util.concurrent.RejectedExecutionException。拒绝执行线程任务。
4. 几个扩展线程池
从以上程序执行情况来看实际上是ThreadPoolExecutor对象负责执行,代码段Executors.newFixedThreadPool(3)是创建ThreadPoolExecutor对象的。只是ThreadPoolExecutor对象有很多有参的构造函数,合理的利用ThreadPoolExecutor的构造函数成为了使用线程池,创建线程池的关键点。Executors提供了很多的静态方法来创建ThreadPoolExecutor对象,其底层实质上就是调用ThreadPoolExecutor不同的构造函数,或者在相同构造函数上使用不同的实参来对外进行服务。这样对于客户端调用者来说不必记住那些复杂的参数含义,按需调用Executors静态方法就能获得自己想要的线程池执行对象,大大简化了创建线程池的难度。这也是装饰器模式的体现,把复杂的东西对客户端以一种简单的方式呈现出来。客户端不必被那些繁琐的参数、创建过程吓到。
1.首先咱们就上面那个程序的例子,上面那个程序创建线程池是如下语句
// 创建线程池 ExecutorService exec = Executors.newFixedThreadPool(3);
它实际上是调用了如下代码
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
大家看到了,客户端调用的时候仅仅传入了一个参数——活动线程的个数。
之前使用newFixedThreadPool的效果大家看到了,在一个时刻,一直是3个线程任务再争抢资源,如果这个时候来新的线程任务了,那么会将其放到LinkedBlockingQueue中放着。LinkedBlockingQueue的大小是整型的最大数——2147483647。这也是最普通的使用场景。限制线程的执行数量,也减少了高并发下多线程之间的资源切换争抢时间。
2.下面是一个仅单线程池的创建例子
// 创建线程池 ExecutorService exec = Executors.newSingleThreadExecutor();
底层为
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
细心地读者发现了,这个和调用
ExecutorService exec = Executors.newFixedThreadPool(1)
没什么区别,这里需要说明的是它的使用场景,这个是单线程的线程池,在一个时刻只能一个线程任务去执行,这个具体的场景在哪里?其实根据实际情况来看,它更适合于做多任务的汇总,这个多任务之间没有任何层次关系,都是并列的。比如云计算节点单机A、节点B、节点C月底凌晨要做流量汇总,运维的时候是要按需收费的!这个时候就需要每个节点机器的用户appid、流量、吞吐汇报上来做报表汇总。此时数据上报是多线程任务的,从每个节点接收成功后启动计算功能的线程,之后将计算线程逐一放到这个单一的线程池进行计算、入本地库、记日志、单节点的成本BI计算等等操作完成后,下一个node的线程再进行汇总。使用单一线程考虑到2方面,第一每条数据都要严格的进行log记录,以便进行智能的统计BI分析,第二就是在统计的主线程中,需要用到种种局部临时变量,所以这种情况是最适合不过了。当然了,读者要是问我有其他方式解决吗?当然有其他方式。我们这里仅仅讨论newSingleThreadExecutor的适用场景,至少在此场景下newSingleThreadExecutor是解决方案之一。抱歉的是,因为一些特殊的原因,笔者不能将这部分实例代码分享出来,涉及到一些其他利益,大家懂得的,呵呵。
3.还有就是缓存线程池,创建方式如下
// 创建线程池 ExecutorService exec = Executors.newCachedThreadPool();
当放入池中的新线程发现没有可复用的池对象的时候,那么就创建一个新的线程对象放入池中。在一定的时间,默认是60秒后,线程池会自动将其赶出池子,也就是说之前露天的大排档吃完的地方在60s内还可以重复利用。服务员在60s后发现依然没人占用地方。ok,回收之。但是,如果此时线程池依然很忙碌,没有可复用的线程资源,那没办法,只能重新创建一个新线程并添加到池中。
相比较咱们之前的newFixedThreadPool方法,它最大的特点就是回收资源、清除超时(60s)、闲置的线程资源,因此它占用系统资源方面不是很大。而newFixedThreadPool方法,只要线程池不关闭,池子里面的资源不会回收。也就是,您哪怕是买一杯麦当劳的咖啡,也可以在里面占一个座位,喝上一整天,看一整天的书,没办法,谁让咱这个线程执行得慢呢。
4.最后一个要介绍的是类似于Timer类的ScheduledExecutorService,这个就是属于使用周期性任务的线程了,大家对Timer或者任务调度已经很明白了,不再赘述了,这里就将网友的一段代码示例给大家即可
ScheduledExecutorService executor = Executors .newScheduledThreadPool(10); Runnable task = new Runnable() { @Override public void run() { System.out.println("task over"); } }; executor.scheduleAtFixedRate(task, 10, 2, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(task, 10, 2, TimeUnit.SECONDS);的意思就是线程放到周期线程池中,第一次延迟10秒执行,每过2秒则执行一次任务。
5. 总结
线程池的使用其实还可以深入,如果自己感兴趣也可以造造这个轮子。笔者觉得比较难的是创建线程池的时候的创建参数,上面集中线程池是已有的,适合大多数线程池场景的创建方法,如果在特殊情况下需要自己手工new线程池,参数的选择,直接影响了线程池的效率以及功能。最后还是唠叨一下,线程池适合执行短时间连接请求的线程任务,并且访问量比较巨大的情况。对于消耗时间比较长的连接,您最好还是用别的办法。
PS:时间长短是相对来说的,一个笑话:“您觉得一分钟是长还是短?那得看您是在厕所外面排队还是在厕所里面方便……”
各位网友原谅我将其放到了“分布式”的大标题下面,笔者想将其弄成一个系列而已。
评论
分享的目的达到了就好了,我也是想让大家挑错的。至于顶还是踩,我不在乎。说真的,我真希望我没那么排名靠前,烦恼啊……。达到共同进步的目的就好喽。
楼主不必过谦,我一直学习你的博客内容,希望你能及时更新,对我工作挺有促进作用的。
分享的目的达到了就好了,我也是想让大家挑错的。至于顶还是踩,我不在乎。说真的,我真希望我没那么排名靠前,烦恼啊……。达到共同进步的目的就好喽。
是方便吧大哥。
哈哈,lz又来了,继续力挺你的搞笑风格
是方便吧大哥。
哈哈,lz又来了,继续力挺你的搞笑风格
谢谢,希望兄弟你也能提出质疑与批评。
是方便吧大哥。
哈哈,lz又来了,继续力挺你的搞笑风格
哦,sorry,这位兄弟没有看懂吗?看来我表达上还很欠缺,有待加强。
public static ExecutorService newCachedThreadPool()
如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
按照LZ的解释应该是,空闲60s的桌子被收回,而不是坐了60s不管有没吃完都要收回。
太感谢你了,不错,是我理解得不对~
重新解释一下,就是那些60s内没有人坐的桌椅回收。如果在规定时间内——60s,空闲线程是可以被复用的。太感谢了。哈哈,这就是笔者写出来的目的啊!!太感谢了。
public static ExecutorService newCachedThreadPool()
如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。
按照LZ的解释应该是,空闲60s的桌子被收回,而不是坐了60s不管有没吃完都要收回。
是方便吧大哥。
哦,谢谢,不好意思,键盘不好使了,打字快了点……
其实楼主的意思是 吃方便面
啊?不能吧~~这个仅仅是介绍而已,并没太复杂的技术深入。
是方便吧大哥。
哦,谢谢,不好意思,键盘不好使了,打字快了点……
是方便吧大哥。
发表评论
-
Java分布式应用学习笔记09JMX-MBean的介绍(JMX的一点点补充)
2011-10-09 09:01 74571. MBean介绍 从上一篇B ... -
Java分布式应用学习笔记08JMX规范与常用的监控场景
2011-09-13 09:17 91431. JMX规范 JMX是“Java管理扩展的”的缩写,它 ... -
Java分布式应用学习笔记06浅谈并发加锁机制分析
2011-08-19 16:12 80861. 前言 之前总结的多线程的调度、并发调度、线程加锁安全 ... -
Java分布式应用学习笔记05多线程下的并发同步器----后篇
2011-08-11 09:07 70175. CountDownLatch 很多资料上都说Coun ... -
Java分布式应用学习笔记05多线程下的并发同步器----前篇
2011-08-11 09:02 93571. 前言 JDK提供的并发包,除了上一篇提到的用于集合外 ... -
Java分布式应用学习笔记04JDK的并发包的集合总结---后篇
2011-08-02 17:21 4491唉~这一大篇blog又是只能显示部分,部分内容被截断了。。。。 ... -
Java分布式应用学习笔记04JDK的并发包的集合总结---前篇
2011-08-02 17:17 53851. 前言 平时咱们使用的HashMap、ArrayLis ... -
Java分布式应用学习笔记03JVM对多线程的资源同步和交互机制
2011-07-28 10:55 66271. 前言 既然是分布式 ... -
Java分布式应用学习笔记02再谈JVM---续
2011-07-25 09:22 3816唉~~因为blog总显示不全只能分为2个了,排版也不是很好,凑 ... -
Java分布式应用学习笔记02再谈JVM
2011-07-25 09:10 58721. 前言-为何要再谈JVM 很多人认为,分布式Java应 ... -
Java分布式应用学习笔记01分布式Java应用和SOA
2011-07-22 13:52 42531. 前言 当我们所做的 ... -
使用Memcached做分布式系统的Session存储
2011-07-01 10:12 85161. 前言 Memcache除了可以做Hibernate的 ... -
用xmemcache作为JPA(Hibernate实现)二级缓存
2011-06-30 09:44 60561. 持久层的缓存 Hibern ... -
Apache_proxy负载均衡和Session复制
2011-04-06 09:26 10855今天上网查了查资料,之前使用apache的jk模块做负载均衡。 ... -
Java基于线程的分布式(转自cjnetwork)
2011-03-22 17:50 1720java基于线程的分布式 ... -
JBoss集群配置的Session复制
2011-03-21 09:19 59801. 前言 接着上一篇总结文章提出的问题,这次通 ... -
JBoss节点的负载均衡与Mysql主从备份
2011-03-16 22:37 24791. 前言 做JavaEE企业级应用就离不开集群 ... -
在default目录下快速配置JBoss集群(Web方面) 转载
2011-03-06 10:24 2229说起JBoss集群好像很高深的样子,其实一点也不恐怖,建立一个 ...
相关推荐
### Java分布式应用学习笔记07线程池应用 在深入探讨Java分布式应用中线程池的应用之前,我们先来理解一下线程池的基本概念及其在并发编程中的重要性。线程池是Java并发编程的核心技术之一,它通过复用一组预创建的...
Java分布式应用学习笔记 在Java世界中,分布式应用是指由多个独立组件通过网络通信协同工作的系统。这种架构模式常用于构建大规模、高可用性、可扩展的系统。本笔记将深入探讨Java分布式应用的核心概念、技术和实践...
4. **JMS(Java Message Service)**:Java消息服务提供了一种与语言无关的API,用于在分布式环境中发送和接收消息。 5. **JPA(Java Persistence API)**:用于管理数据库持久化操作,简化了对象到关系的映射过程。...
Java分布式与微服务实战\多线程与分布式\第1节 线程池 线程池是Java中的一种重要的多线程机制,用于管理和控制线程的创建、运行和销毁。线程池的出现可以解决多线程带来的问题,例如反复创建和销毁线程所带来的开销...
从给定的文件信息来看,标题和描述都指向了“Java分布式学习笔记01分布式Java应用”,这显然是关于Java在分布式环境下的应用和技术的学习资料。虽然提供的部分内容由于格式问题难以直接解析,但我们可以根据标题、...
【Java分布式应用学习笔记-谈JVM】 在Java分布式应用中,JVM(Java虚拟机)扮演着至关重要的角色。虽然有些人可能认为分布式系统与JVM的关系并不密切,但事实上,尤其是在大型分布式环境,如云计算服务平台,对Java...
Java 分布式应用程序设计是构建大型、可扩展和高...以上只是Java分布式应用程序设计的一些核心知识点,实际应用中还会涉及到缓存管理、数据库分库分表、安全认证、限流熔断等多个方面,需要不断学习和实践来提升技能。
13. **CDI(Contexts and Dependency Injection)上下文和依赖注入**:Java EE的一部分,CDI帮助管理对象的生命周期和依赖关系,简化了分布式系统中的组件装配。 以上知识点涵盖了Java分布式应用程序设计的核心概念...
另一方面提供了进一步学习这些知识点的参考资料,希望能给想掌握编写分布式Java应用知识点的开发人员提供一定的帮助以及指引,同时也希望书中分享的经验对于目前正在从事分布式Java应用编写的开发人员提供帮助。
在"分布式Java应用基础与实践"这本书中,读者可以深入学习这些概念,并通过实例学习如何在实际项目中应用。无论是初学者还是经验丰富的开发者,都能从中受益,提升自己的分布式系统设计和开发能力。
《分布式Java应用基础与实践》pdf电子版,
本资源包含一本名为“JAVA分布式程序设计”的电子书以及相关的源码示例,旨在帮助开发者深入理解和实践Java在分布式环境中的应用。 《JAVA分布式程序设计》这本书可能涵盖了以下几个核心知识点: 1. **分布式系统...
本示例("java分布式事务demo")很可能是为了演示如何在Java应用程序中实现这一功能。 分布式事务处理通常涉及ACID(原子性、一致性、隔离性和持久性)原则,这些原则是传统数据库事务管理的基础。在Java中,实现...
java
分布式Java应用是计算机科学中的一个重要领域,它利用了Java语言的网络友好性,为实现大型、高并发、高性能的系统提供了可能。分布式计算技术有着悠久的历史,它从早期的C/S架构,到P2P模型,再到现在的云计算,始终...
- **网络编程**:Java的Socket编程接口使得开发者可以方便地创建网络通信应用程序。 2. **分布式系统的关键组件** - **负载均衡**:如Nginx或HAProxy,它们可以均匀分配请求到多个服务器,提高系统的处理能力。 ...