#
1、同步
线程初始化时,可以向线程传入对象,就存在多条同类型的线程存取同一对象,带来对象的值不同步的问题。
JVM方案,采用锁机制,线程的run代码里,使用同步关键字,同步该对象,由于一个对象只有一个锁,没取得锁的线程,就只能处于等待的状态了,run方法执行完,则锁就被释放了,下一个线程是哪个,不确定,由他们进行竞争锁,谁取得,谁就可以先上。
2、wait()
这是Object的方法,执行这个对象的wait()方法并不是去执行该对象里面的wait方法,而是通知JVM暂停锁定了该对象的线程的run方法,进入等待状态,同时也释放该锁,使得另外的同类线程有机会执行
3、synchronized
在方法前加synchronized,相当于线程的run方法中先同步资源对象,再且仅执行一个该方法。
4、start()
调用线程的start方法,并不会立即执行run方法,而是由JVM决定JVM里那一堆线程,哪个先执行,再执行run方法
这是一份历尽千辛万苦,迟来却依旧让人无比激动的圣诞礼物。就在大约1个小时之前,Pod 2g的博客终于迎来了更新,标题更是醒目的“A4 release”,等待终于结束了。
Pod 2g在博客中称,基于他的研发成果,Chronic Dev Team(带头大哥p0sixninja,代表作绿毒)和iPhone Dev Team(带头大哥肌肉男,代表作红雪)经过测试后,于今天正式发布A4设备的iOS 5.0.1完美
越狱。
无论是已经不完美
越狱的用户,还是从未使用RedSn0w
越狱的用户,均可使用本次
越狱方案进行完美
越狱。支持设备:iPhone 3GS/4,iPad1代,iPod touch 3/4。与此同时,Pod 2g建议大家在苹果发布iOS 5.0.2时不要升级,以免
越狱漏洞被封堵。
越狱工具下载,请点击跳转。 使用最新版RedSn0w完美
越狱iOS 5.0.1教程:
首先当然是要将手上的设备升级到iOS 5.0.1了。向小白温馨提示:不要直接连接iTunes之后点击升级5.0.1,因为这样一来设备上的部分软件还会保留,不利于后面的
越狱。正确做法:直接通过iTunes安装苹果官方的5.0.1固件即可。
第一步:从我们给出的链接地址下载Mac/Windows版RedSn0w 0.9.10b1。解压后打开redsn0w.exe,点击Jailbreak。
第二步:此时要确保需要
越狱的设备与电脑连接,点击Next
第三步:进入DFU模式。具体操作方法RedSn0w已经给出:先按电源键三秒左右,等待跳转之后再按下Home键,此时不要放开电源键,再次跳转之后放开电源键,Home键保持不动。
第四步:最后放开Home键之后就是验证设备和准备
越狱的过程了,需要花上一点时间。
第五步:验证完成后,出弹出以下图框,第一个打勾选项为安装Cydia,此时点击Next。这时候RedSn0w会上传RAM Disk和Kernel,最后就是进行
越狱了,整个过程估计耗时5分钟左右。
第六步:此时iPod touch 4(iOS 5.0.1)已完美
越狱。
iOS 5.0.1完美
越狱方法2:
以上是使用最新版本的RedSn0w v0.9.10b1进行的完美
越狱。假如你的设备之前已经升级iOS 5.0.1,并且已通过RedSn0w进行过不完美
越狱,那么这里还有一种来自绿毒团队Chronic Dev Team的方法,支持iPhone 3GS/4, iPhone 4 CDMA, iPad 1, iPod touch 3/4。这种方法更为简单直接:
第一步:进入Cydia,搜索到Corona Untether 5.0.1
第二步:点击安装,Cydia会帮你完成接下来的工作
没有第三步了。
完美
越狱完成,你现在已经可以安全重启这些设备了。
Cydia成功出现在iOS 5.0.1系统当中,还只是第一步而已。各位在
越狱之后,先不要急着进入Cydia,因为Cydia在IOS5.0+的固件下,添加源会闪退,以下是处理方法:
点击设备中的设置→通用→多语言环境→语言中,选择English(英文),进入英文环境。这时候Cydia不会出现闪退,所以可以在这个时候添加源。
加入源的步骤如下:
第一步:点击下方Manage(管理),进入Source(软件源)
第二步:点击右上方Edit(编辑)
第三步:点击左上方Add(加入)
第四步:弹入方框后输入apt.weiphone.com(威锋源)后,点击Add Source(加入软件源)
第五步:如果弹出一个警告窗口,请无视它,点击Add Anyway(仍然加入),之后就会进入源的安装中了。
第六步:安装完成之后,点击右下的Search(搜索),输入afc2,就会出现一个叫做“afc2服务补丁”的文件。
第七步:点击进入“afc2服务补丁”的文件之后,点击右上的Install(安装),跳转页面之后在点击右上角的Confirm,之后就是安装文件了。
第八步:到了这步,大家可以下载iFunBox了(
iFunBox下载地址请点击跳转)。下载完成后,打开iFunBox,选择路径文件系统/var/mobile/Library中找到Keyboard文件夹,并将其拖出桌面。
第九步:然后再选择路径文件系统/var/root/Library,将桌面的Keyboard文件夹拖入其中。
OK了,完成这步之后,就解决了你在简体中文环境下,iOS5.0+系统使用Cydia崩溃的情况。之后就能在切换回简体中文了。最后可不要忘了安装 AppSync for iOS 5.0,只要在搜索中搜索就能看见,点击安装之后就能安装破解过后的后缀名为ipa的软件了。
最后,我们仍然要再次重申的是,本次
越狱仅支持iOS 5.0.1。大神的博客里写得很清楚了,5.0的完美
越狱目前还在测试之中,相信不久之后就会放出。另外,两个版本的
越狱都不支持iPhone 4S和iPad 2,希望大家注意了。
周末听了项目管理的课程,很有感触,有很多记忆深刻的点,比如不要揣摩要提问,要先管理后产品,胜者先胜而后战,败者先战而求胜。然而,让我印象最深的是估算工时这点事,不禁让我想起自己的经历来。
最开始刚参加工作时是在一家翻译公司做它内部的协同系统,两个程序员,老大和我,老大是30岁的老程序员,安排工作很随性,每天早上,点点系统,然后想想接下来要做什么,然后,叫上我,说,把这棵树实现一下吧,然后,再说,需要多长时间?我想一会儿,不就是一棵树吗,说,1小时。老大点点头,说,好,1天。再一天,老大又交给我一项任务,这次任务量大一些,要实现一个完整的模块,我同样是想了想,说3天,老大点点头,说,好,9天。老大总是这样,只要是我作出的估算,他总要乘以3,对此我是不以为然的,这也太保守了。不过好消息是,一年下来我们从未加过班,几乎所有的工作都按计划完成了,日子过得轻松而又悠闲,经常有些时间写点自己喜欢的其他代码,一点也不像程序员。
第二份工作就是IT公司了,做企业应用开发。老大使用干特图来进行工期管理,项目开始之前,老大已经分好大的模块了,这些模块比我第一家公司所谓的模块要大的多,需要以周来估算。这天,老大叫上我,问,这个门户的模块需要几周完成?我想一想,门户以前没有接触过,学习需要一周,开发需要两周,再留一周测试,说,四周。老大点点头,说,好,我再给你加一周学习时间,于是用鼠标在project上拖出一条长长的蓝线来。几个程序员每个都拖出一条蓝线来,三个程序员,八个模块,蓝线短的就再估算一个模块,这样,一条一条蓝线拼起来,最长的那条就成了关键路径,也决定了交付日期,交付日期定在五个月以后。于是就开始开发,那时年轻,无知者无畏,还是单身,加班就不是问题,三个人周六都在公司,吭嗤吭嗤,还真在五个月后给吭嗤完了。于是开始请测试妹妹测试,这一测试问题就来了,很多模块根本就是不可用,只是能够增删改查,很多功能点都没有考虑到,易用性就更别谈了,也难怪,系统设计、实现、测试都被一个只会写代码的程序员包了,既当运动员又当裁判员,能想清楚才怪,于是继续吭嗤,这不吭嗤还好,一吭嗤不得了,又吭嗤出五个月来,再看看jira,几百个bug,每个人都很绝望,老大手一挥,说,哪个软件没有bug,停止开发,修复最重要的bug,然后发布。
第二个项目做完,开始反思,为什么延期这么长估算这么不准,想了很久,觉得是因为估算的粒度太大,时间超过一周,对估算的开发量实际就失去计算了,只剩下一个大概的印象,而为了不成为关键路径不在老大面前丢脸,拍胸脯的情况也发生了,没事,加几个班就搞定了。同时,缺少系统需求说明,这样,在一个错误时间内完成一个不可能完成的任务,质量可想而知,赶进度献礼工程不仅政府在做,程序员同样在做。
这样,到了第三年,自己开始负责一个项目了,吸取了教训,开发估算时,估算的工作量不能超过1周,一旦超过就需要继续分解再进行估算。估算下来,这个项目需要四个月完成,又想起第一个老大的乘三法则,心里颤了一下,难道需要12个月?不能这样,这样这个项目就被取消了。自己带了侥幸的心理,团队的成员都很强,事后也证明这个团队的个人能力都很强,因为有两个人现在在知名外企,一个在淘宝,一个在腾讯,小公司组成这样豪华的阵容是很难得的,唯一不幸的是,我是那个项目经理。我把交付时间定在了四个月之后,开发进度整体还是顺利的,稍微有延期,每周需要延一到两天,这样,我把时间又延长了一个月。天知道,意外发生了,因为我的一句批评,一个成员离职了!我痛哭的发现,项目又要延一个月了,于是,接下来,我就流涕了,六个月时间,公司没有那么多钱投入了!我开始加班,要求其他人也加一些班,意外接着发生,有人生病了,有人有急事要请假了,到最后,终于发现,按估算完成是完全不可能的。最后,项目就被取消了,悲催的项目经理。
继续反思,为什么估算不准,这次我自以为原因很清楚,就是没有考虑项目风险,没有考虑到人会离职,没有考虑到人会请假,没有考虑到人会生病,甚至,没有考虑到人一天工作的产出不是八小时。我太乐观了。我再一次想起来我的第一个老大。
新的公司,新的估算方式,不再一个人估算而是一伙人估算,特性不估算,故事点再估算,没有需求不估算,不一次性估算,迭代估算。这次似乎没有问题了,但客户总是需要一个大的时间点的,于是每个迭代都会排定故事优先级,确保交付前交付的是最有价值的,此外,还有专职的业务分析和漂亮的测试美眉,一切看起来都很好。新公司第一个项目也确实轻松的,但第三个和第四个却都失败了,第三个项目在遇到一个很难解决的性能问题时陷入了一片混乱当中,迭代经理甚至自己都失去对整个项目的可视化了,她不知道项目上线究竟需要满足什么条件,于是项目在一次次的下周二上线的空头承诺中成了整个公司的笑柄。幸福的项目总是相似,不幸的项目各有各的不幸。第四个项目在一次项目中期的架构重写中崩溃了,重写耗去了团队太多的时间,而由于对未知领域知识的不正确估算再次令项目雪上加霜,而更加致命的是,项目目标直到最后一刻也没有发生变化,依旧是要在六个月后上线,于是,程序员们就内存溢出了。
乐观、管理混乱、领域知识不熟,这些都导致了项目延期,工时等于工期吗?不等于,工期永远是假的,工时估计的准确,混乱的管理同样会毁掉它。更严重的不是项目延期,而是项目本来就是老板拍脑袋的结果,想想某个项目,如果不说我们能快速交付就根本得不到它,而得到它后怎么办,那只能拜春哥了!
摘要: 所需软件 Apache :apache_2.0.63 1 个 【apache_2.0.63-win32-x86-no_ssl.msi】 Tomcat: apache-tomcat-5.5.23 (zip版) 2个 mod_jk:: mod_jk-apache-2.0.55.so 1个 部署说明 在同一台机器上部署,即充当Apache负载服务器,又安装两个Tomcat Web服务...
阅读全文
在application server下,比如常见的weblogic,glassfish,jboss等,由于javaee规范的要求,一般不容许直接启动线程。因此在常见的异步/并行任务执行上,会遭遇到比普通javase程序更多的麻烦。
典型例子,在javase中,jdk1.5后就引入了java.util.concurrent包,提供Executor这个非常好用的框架,完美的满足一下典型需求:
1. 同步变异步
请求进来后,将请求封装为task,交给Executor执行,原线程可以立即返回
2. 并行执行
请求进来后,将请求拆分为若干个task,例如下发短信,有100个收件人就可以按照每个收件人一个task来执行,这样可以通过Executor来并行执行这些请求,远比循环执行要快的多。
3. 等待任务结束
有时有要求调用线程必须等待所有任务完成后再继续运行的需要,此外还有超时等细节设置要求。
而在application server,为了避开自己启动线程的弊端,只好通过其他的方式来完成类似的功能。
目前我们的项目开发中主要有三种实现方式:
1. jms queue
通过jms来实现异步和并发,然后自己通过编码方式完成调用线程等待所有任务执行成功。
这个方案比较通用,因为jms是javaee的标准,所有的application server上都支持。因此天然具有跨application server的能力。
缺点就比较多了,首先jms是需要实现串行化的,因此对task是有要求,不能串行化的类是不能传递的。另外串行化的性能损失比较大,造成性能和稳定性问题,这个在大压力下比较突出,基本我们目前在考虑放弃这个方案,而且逐步将原有的实现替换掉。
这个方案还有另外一个缺点,配置麻烦,维护困难:需要创建jsm queque, connection factory, MDB等,如果系统中使用的多了,配置起来很罗嗦,修改时容许出错。
2. commonj work manager
这个是weblogic和WebSphere上支持的一个很实用的解决方案,个人感觉使用上非常舒服,配置简单,只要在weblogic-ejb-jar.xml中间中简单配置:
< work-manager >
< name > wm/taskDistributionWorkManager </ name >
< min-threads-constraint >
< name > minthreads </ name >
< count > 1 </ count >
</ min-threads-constraint >
< max-threads-constraint >
< name > maxthreads </ name >
< count > 100 </ count >
</ max-threads-constraint >
</ work-manager > 使用时用jdni lookup到就可以使用了。功能和使用方式和executor框架很类似,同样提供future,而且提供一个非常实用的waitAll()方法论支持等待任务完成。
这个方案的性能非常好,和jms相比提升极大,运行也稳定。缺点就是不是标准,只有weblogic和WebSphere执行,在glassfish,jboss上无法使用。
3. JCA work manager
这个是JCA标准了,glassfish,jboss都支持的,和commonj work manager很像,但是,很遗憾的是没有future的支持,而且也没有类似的waitAll()方法,只能自己编码实现。
spring为glassfish提供了一个工具类"org.springframework.jca.work.WorkManagerTaskExecutor",简化了JCA work manager的使用。
JCA work manager的性能和稳定性都还不错,对比jms要好的多。
JBOSS下的配置
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<resource-ref id="ResourceRef_1163654014164">
<description>WorkManager</description>
<res-ref-name>jboss.jca:service=WorkManager</res-ref-name>
<res-type>org.jboss.resource.work.JBossWorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
<jndi-name>WorkManager</jndi-name>
</resource-ref>
</jboss-web>
applicationContext.xml
<bean id="taskExecutor" class="org.springframework.jca.work.jboss.JBossWorkManagerTaskExecutor">
<property name="workManagerName" value="WorkManager"/>
<property name="resourceRef" value="true"/>
</bean>
从目前我们项目的使用经验上看,jms是准备要被淘汰的了(其实我是一直对jms不感冒的,尤其是在有性能要求的地方,想不出用jms的理由)。目前项目要求同时支持weblogic和glassfish,因此commonj work manager和JCA work manager刚好对应于weblogic和glassfish平台。实际使用中,是在这两个work manager上封装了一个通用的接口,然后再有commonj work manager和JCA work manager两个实现,在运行时通过判断平台来自动选择注入其中的一个。
摘要: 用ThreadPoolExecutor的时候,又想知道被执行的任务的执行情况,这时就可以用FutureTask。ThreadPoolTask
Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->package com.paul.threadP...
阅读全文
这是一堂关于UML基础知识的补习课;现在我们做项目时间都太紧了,基本上都没有做过真正的class级别的详细设计,更别提使用UML来实现规范建模了;本篇主要就以前自己一直感觉很迷糊的几种class之间的关系进行整理,让我们在真正用UML进行比如类图设计时能够更加清晰明了;以下就分别介绍这几种关系:
继承
指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;
实现
指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接口之间最常见的关系;在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性;
依赖
可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作为参数被类A在某个method方法中使用;
关联
他体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友;这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的、关联可以是单向、双向的;表现在代码层面,为被关联类B以类属性的形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量;
聚合
聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
组合
组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;他同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;比如你和你的大脑;表现在代码层面,和关联关系是一致的,只能从语义级别来区分;
对于继承、实现这两种关系没多少疑问,他们体现的是一种类与类、或者类与接口间的纵向关系;其他的四者关系则体现的是类与类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准备定位是很难的,前面也提到,这几种关系都是语义级别的,所以从代码层面并不能完全区分各种关系;但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖;
大多数并发应用程序是以执行任务(task)为基本单位进行管理的。通常情况下,我们会为每个任务单独创建一个线程来执行。这样会带来两个问题:一,大量的线程(>100)会消耗系统资源,使线程调度的开销变大,引起性能下降;二,对于生命周期短暂的任务,频繁地创建和消亡线程并不是明智的选择。因为创建和消亡线程的开销可能会大于使用多线程带来的性能好处。
一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作用是提供一种缓冲机制,将没有处理的任务放在任务队列中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。下面的代码实现了创建一个线程池,以及从线程池中取出线程的操作。
在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多对并发特性的支持,例如:线程池。
1.核心线程(任务):我们定义的线程,即实现了Runnable接口的类,是我们将要放到线程池中执行的类,如实例代码中的CountService类
2.工作线程:由线程池中创建的线程,是用来获得核心线程并执行核心线程的线程(比较拗口哦,具体看代码就知道是什么东东了)。
简单理解就三个概念:线程、线程池和任务。任务:就是要执行的业务逻辑;线程:任务是要放到线程中去执行的;线程池:主要是控制当前正在执行的线程的数量和将要被执行的线程队列。一、简介
线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
ThreadPoolExecutor.AbortPolicy()
抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()
重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy()
抛弃当前的任务
二、一般用法举例
package com.paul.threadPool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThreadPool {
private static int produceTaskSleepTime = 10;
private static int produceTaskMaxNumber = 10;
public static void main(String[] args) {
// 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 1; i <= produceTaskMaxNumber; i++) {
try {
String task = "task@ " + i;
System.out.println("创建任务并提交到线程池中:" + task);
threadPool.execute(new ThreadPoolTask(task));
Thread.sleep(produceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.paul.threadPool;
import java.io.Serializable;
public class ThreadPoolTask implements Runnable, Serializable {
private static final long serialVersionUID = 0;
// 保存任务所需要的数据
private Object threadPoolTaskData;
private static int consumeTaskSleepTime = 2000;
ThreadPoolTask(Object tasks) {
this.threadPoolTaskData = tasks;
}
public synchronized void run() {
// 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
System.out.println("开始执行任务:" + threadPoolTaskData);
try {
// //便于观察,等待一段时间
Thread.sleep(consumeTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
threadPoolTaskData = null;
}
public Object getTask() {
return this.threadPoolTaskData;
}
} 说明:
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask类型的对象。
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个残忍的领导,他派发出许多任务,丢给一个叫 threadPool的任劳任怨的小组来做。
这个小组里面队员至少有两个,如果他们两个忙不过来,任务就被放到任务列表里面。
如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。但是基于成本的考虑,不能雇佣太多的队员,至多只能雇佣 4个。
如果四个队员都在忙时,再有新的任务,这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发,直到接受这个任务为止(更残忍!呵呵)。
因为队员工作是需要成本的,如果工作很闲,闲到 3SECONDS都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。
4、通过调整 produceTaskSleepTime和 consumeTaskSleepTime的大小来实现对派发任务和处理任务的速度的控制,改变这两个值就可以观察不同速率下程序的工作情况。
5、通过调整4中所指的数据,再加上调整任务丢弃策略,换上其他三种策略,就可以看出不同策略下的不同处理方式。
6、对于其他的使用方法,参看jdk的帮助,很容易理解和使用。