EAI中的应用集成和过程集成

书上抄网上,网上互相抄,一模一样的话,就是没看懂,还是这个解释的稍微清楚点:

应用层集成
应用层集成主要是指在通过应用接口对应用系统实现集成。应用接口(Application Programming Interface - API)是通用应用系统以及客户自建系统为方便和外部应用系统连接而对外开放的软件接口。目前时常上的一些标准商业软件,例如ERP系统,CRM系统,电子商务系统等,都有非常成熟的API。通过采用API层的应用集成,用户可以不对应用的数据库直接进行操作。

过程集成
过程集成是将不同单位部门、或不同企业的不同业务流程利用应用集成技术集成在一起,实现跨部门、跨系统、跨企业的流程共用。

Continue reading EAI中的应用集成和过程集成

面向对象的原则

1、"开-闭"原则——模块应对扩展开放,而对修改关闭。
2、里氏代换原则——如果调用的是父类的话,那么换成子类也完全可以运行。里氏代换原则是继承复用的一个基础。
3、合成复用原则——要少用继承,多用合成关系来实现。 --这也造成结构复杂,实现成本增加
4、依赖倒转原则——抽象不应该依赖与细节,细节应当依赖与抽象。 要针对接口编程,而不是针对实现编程。
5、接口隔离原则——每一个接口应该是一种角色,不多不少,不干不该干的事,该干的事都要干。
6、单一职责
7、迪米特法则——最少知识原则。不要和陌生人说话。--但这也造成不同模块通信效率低,产生大量传递简介调用的小方法。

 

这不过是对面向对象精髓的阐述:模块化,抽象,信息隐藏,高内聚,低耦合。

Continue reading 面向对象的原则

NoSQL对REST的影响,无状态,扩展性

infoq文章 http://www.infoq.com/news/2011/10/nosql-rest 谈到noSql可能对REST产生的影响。

REST要求状态要么被放入资源状态中,要么保存在客户端上。而这个资源就可以使用NoSql来存储,像Redis。

无状态通信最直接的理由就是可伸缩性—— 如果服务器需要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。

 

参见:

http://www.infoq.com/cn/articles/rest-introduction  深入浅出REST

http://a-kuei.iteye.com/blog/706836  对REST中无状态(stateless)的理解

Continue reading NoSQL对REST的影响,无状态,扩展性

REST 将替代 SOAP?

infoq上看到这篇文章http://www.infoq.com/articles/rest-soap,其统计数据表明REST(在SOA中)越来越占主导优势。并在文章结尾为mule打了个广告,原来作者是MuleSoft的创始人。文章评论很多,打头的把emule和mule混淆,认为eMule(应该是mule)仍然解决不了根本的集成问题,后面的拿这个开涮!

写这篇文章时我对REST早闻其名,最近也用过,但是自己还觉得没怎么透彻的理解。不过由于早年深受webservice欺骗和xml的虐待,对soap一直深恶痛绝。使用rest基于json格式的服务后,感觉如此之美。

大执概括一下这篇文章:

Web API越来越火从2005的105个到2011的5000以上。

REST风格从2008年开始急剧上身(60%->74%),而SOAP上升缓慢(25%->15%)。

REST的上升要得益于客户端访问的便捷,json格式要比xml格式易懂,易用,简洁。目前大部分api都提供或只提供json格式。

后面就开始说各Api集成问题,然后提出使用mule解决。

Continue reading REST 将替代 SOAP?

冒烟测试 smoke test

没听过这个词的认为说它的人很牛X,了解了后会认为是装X,原来就是这么easy。

冒烟测试的对象是每一个新编译的需要正式测试的软件版本,目的是确认软件基本功能正常,可以进行后续的正式测试工作。冒烟测试的执行者是版本编译人员。

上面一句话很经典,冒烟测试:

  • 费时很短,吸根烟的功夫。
  • 是正式测试前的检验步骤,这个都没通过那就不能进行测试流程。
  • 关注改动,由编译人员或是修改者执行。

Continue reading 冒烟测试 smoke test

三级封锁协议两段锁以及隔离级别

并发控制的主要方法是封锁(Locking)。就是要用正确的方式调度并发操作,使一个用户的事务在执行过程中不受其他事务的干扰,从而避免造成数据的不一致性。
封锁是使事务对它要操作的数据有一定的控制能力。封锁通常具有3个环节:第一个环节是申请加锁,即事务在操作前要对它将使用的数据提出加锁申请;第二个环节是获得锁,即当条件成熟时,系统允许事务对数据进行加锁,从而事务获得数据的控制权;第三个环节是释放锁,即完成操作后事务放弃数据的控制权。
基本的封锁类型有以下两种: 锁是事务实现并发控制隔离级别的实现方法
1.排它锁(Exclusive Locks,简称X锁)
排它锁也称为独占锁或写锁。一旦事务T对数据对象A加上排它锁(X锁),则只允许T读取和修改A,其他任何事务既不能读取和修改A,也不能再对A加任何类型的锁,直到T释放A上的锁为止。
2.共享锁(Share Locks,简称S锁)
共享锁又称读锁。如果事务T对数据对象A加上共享锁(S锁),其他事务只能再对A加S锁,不能加X锁,直到事务T释放A上的S锁为止。
在对数据进行加锁时,另外需要约定并执行一些规则和协议,其中包括何时申请锁,保持锁的时间以及何时释放等,这些规则就称为封锁协议(Locking Protocol){谁定义的?-确实找不出来,也许就是理论基础},其总共分为以下三级:
(1)一级封锁协议。一级封锁协议是事务T在修改数据之前必须先对其加X锁,直到事务结束才释放。 防止丢失更新。
(2)二级封锁协议。二级封锁协议是事务T对要修改数据必须先加X锁,直到事务结束才释放X锁;对要读取的数据必须先加S锁,读完后即可释放S锁。 防止丢失更新和脏读

 T1T2 
 Slock A  
 读A=20
unlcok A 事务还没完成,但是读完了马上释放
  
  Xlock A 
  A = 60 
  Unclock A
事务提交
 
 SlockA  
 A=60
不可重复读
  
 T1事务造成了不可重复读  

(3)三级封锁协议。三级封锁协议是事务T在读取数据之前必须先对其加S锁,在要修改数据之前必须先对其加X锁,直到事务结束后才释放所有锁。 防止丢失更新,脏读,不可重复读

相对于二级锁,Slock的范围加长了,开销自然大了。

执行了封锁协议之后,就可以克服数据库操作中的数据不一致所引起的问题。

参看图4。

Snap2

从图4的情况我们看到事务T1在执行过程中独自占用并加X锁,直到处理完之后再释放锁,T2虽然也需要使用,但是在封锁协议的约束之下,T2所要求的X 锁就被拒绝,因此必须处于等待状态,直到T1释放之后,T2才获得使用的权利,这样就不会发生使用冲突,避免了数据的丢失。这里我们看到,此处实际上是执行了一级封锁协议。

下面我们看图5。

Snap3

通过图5,能够清楚的看到,由于施行了封锁协议,使事务T1使用了共享锁占用A,B两块数据,这样T2需要加上的X锁就无法实现,(如果是S锁,虽然可以加上,但也不能够随便修改数据,只是读取一下数据。)当T1释放锁之后,T2就可以得到并使用锁了,这样读取的数据B仍然还是100,不影响A+B的结果,这就是可重复读取。因此我们看到,其实这里用的就是三级封锁协议

 

参看图6,事务T1在对数据C修改之前,先加上了X锁,修改后写回数据库,这时T2请求在C上添加S锁,因为T1加了X锁,T2只好等待,当T1因为某种原因撤销了修改的数据后,C就恢复了原来的数据100,等T1释放 X锁后T2获得C上的S锁,读到的还是C=100,因此避免了读出“脏”数据。这里使用的其实就是二级封锁协议。

Snap1

 

两阶段封锁协议

对锁机制,保证事务可串行性的最常用协议是两阶段封锁协议。该协议要求每个事务分两个阶段提出加锁和解锁申请:

(1)增长阶段。事务可以获得锁,但不能释放锁。

(2)缩减阶段。事务可以释放锁,但不能获得锁。

一开始,事务处于增长阶段,事务根据需要获得锁。一旦该事务释放了锁,它就进入缩减阶段,不能再发出加锁请求。

两阶段封锁协议实现了事务集的串行化调度,但同时,一个事务的失败可能会引起一连串事务的回滚。为避免这种情况的发生,我们需要进一步加强对两阶段封锁协议的控制,这就是:严格两阶段封锁协议和强两阶段封锁协议。

严格两阶段封锁协议除了要求封锁是两阶段之外,还要求事务持有的所有排它锁必须在事务提交之后方可释放。这个要求保证未提交事务所写的任何数据,在该事务提交之前均以排它锁封锁,防止其他事务读取这些数据。

强两阶段封锁协议,要求事务提交之前不得释放任何锁。使用锁机制的数据库系统,要么使用严格两阶段封锁协议,要么使用强两阶段封锁协议。

两阶段封锁协议并不保证不会发生死锁,数据库系统必须采取其他的措施,预防和解决死锁问题。

 

数据库并发操作存在的异常情况:

1. 更新丢失(Lost update):事务 T1 读取数据 A,然后对 A 进行运算修改,最后写回数据库。如果在 T1 读取和写回数据库之间,有其他事务修改了 A 值,就造成了丢失更新,因为 T1 是在旧的数据上进行的运算。

第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。

第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

说他是第二类更新,又说他其实是不可重复读的特例,实际上在隔离级别上又和不可重复读相同,大部分分类根本不提这个第二类丢失更新。

2. 脏读取(Dirty Reads):一个事务读取了另一个未提交的并行事务写的数据。事务 T1 修改了数据 A,然后事务 T2 读取了数据 A,然后事务 T1 回滚了事务。则T2读的是错误的数据。

3. 不可重复读取(Non-repeatable Reads):一个事务对同一行数据重复读取两次但是却得到了不同结果。例如在两次读取中途有另外一个事务对该行数据进行了修改并提交

4. 幻读(Phantom Reads):也称为幻像(幻影,虚读)。事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的。

幻读和不可重复读可认为是同类的,但是在控制上有区别。要控制不可重复读只需要控制记录的修改,而要控制幻读则要控制记录的添加和删除。所以,隔离级别可重复读取不能禁止幻读,而串行则可以。

 

事务隔离级别:

为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。

1. 未授权读取(Read Uncommitted):也称未提交读。防止更新丢失(这不对应一级锁吗),如果一个事务已经开始写数据则另外一个数据则不允许同时进行写操作但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。事务隔离的最低级别,仅可保证不读取物理损坏的数据。与READ COMMITTED 隔离级相反,它允许读取已经被其它用户修改但尚未提交确定的数据。

2. 授权读取(Read Committed):也称提交读。1之上防止脏读取(这不对应二级锁吗)。这可以通过“瞬间共享读锁”和“排他写锁”实现,读取数据的事务允许其他事务继续访问该行数据,但是未提交写事务将会禁止其他事务访问该行。SQL Server 默认的级别。在此隔离级下,SELECT 命令不会返回尚未提交(Committed) 的数据,也不能返回脏数据。

3. 可重复读取(Repeatable Read):2之上防止不可重复读取(这不对应三级锁吗)。但是有时可能出现幻影数据,这可以通过“共享读锁”和“排他写锁”实现,读取数据事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。在此隔离级下,用SELECT 命令读取的数据在整个命令执行过程中不会被更改。此选项会影响系统的效能,非必要情况最好不用此隔离级。

三级封锁协议并不能阻止幻读,修改的不能再被读取,但是新增(删除)的记录数可以统计。

4. 串行(Serializable):也称可串行读(这不对应两段锁吗)。提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过 “行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作事务访问到。事务隔离的最高级别,事务之间完全隔离。如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠事务均是串行的。

 

 LU丢失更新DR脏读NRR非重复读SLU二类丢失更新PR幻像读
未提交读 RUYYYYY
提交读 RCNNYYY
可重复读 RRNNNNY
串行读 SNNNNN

ORACLE的默认事务级别:READ COMMITTED

ORACLE支持的事务隔离级别:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION ISOLATION LEVEL READ ONLY;

少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎
即使是最低的级别,也不会出现 第一类 丢失 更新问题 .

查看InnoDB系统级别的事务隔离级别:

mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set (0.00 sec)

查看InnoDB会话级别的事务隔离级别:

mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

修改事务隔离级别:

mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

InnoDB的可重复读隔离级别和其他数据库的可重复读是有区别的,不会造成幻象读(phantom read)。

 

Oracle对隔离级别的支持:

Oracle 明确地支持READ COMMITTED(读已提交)和SERIALIZABLE(可串行化)隔离级别,因为标准中定义了这两种隔离级别。不过,这还不是全部。SQL标准试图 建立多种隔离级别,从而允许在各个级别上完成的查询有不同程度的一致性。REPEATABLE READ(可重复读)也是SQL标准定义的一个隔离级别,可以保证由查询得到读一致的(read-consistent)结果。在SQL标准的定义 中,READ COMMITTED不能提供一致的结果,而READ UNCOMMITTED(读未提交)级别用来得到非阻塞读(non-blocking read)。

不 过,在Oracle中,READ COMMITTED则有得到读一致查询所需的所有属性。在其他数据库中,READ COMMITTED查询可能(而且将会)返回数据库中根本不存在的答案(即实际上任何时间点上都没有这样的结果)。另外,Oracle还秉承了READ UNCOMMITTED的“精神”。(有些数据库)提供脏读的目的是为了支持非阻塞读,也就是说,查询不会被同一个数据的更新所阻塞,也不会因为查询而阻 塞同一数据的更新。不过,Oracle不需要脏读来达到这个目的,而且也不支持脏读。但在其他数据库中必须实现脏读来提供非阻塞读。

        除 了4个已定义的SQL隔离级别外,Oracle还提供了另外一个级别,称为READ ONLY(只读)。READ ONLY事务相对于无法在SQL中完成任何修改的REPEATABLE READ或SERIALIZABLE事务。如果事务使用READ ONLY隔离级别,只能看到事务开始那一刻提交的修改,但是插入、更新和删除不允许采用这种模式(其他会话可以更新数据,但是READ ONLY事务不行)。如果使用这种模式,可以得到REPEATABLE READ和SERIALIZABLE级别的隔离性。
        所以,Oracle对隔离级别的支持如下:
1.SERIALIZABLE:支持
2.READ ONLY:Oracle特有的级别,利用它来实现对REPEATABLE READ的支持
3.READ COMMITTED:支持
4.READ UNCOMMITTED:不明确且不完全地支持

 

参见:

http://seaizon.iteye.com/blog/571139

http://wenku.baidu.com/view/2f89710879563c1ec5da7130.html

http://yjhexy.iteye.com/blog/658706

http://www.cnblogs.com/ityfei/articles/1502153.html

http://heysql.com/mysql/%E5%9F%BA%E7%A1%80%E7%9A%84%EF%BC%9A%E5%B0%81%E9%94%81%E5%8D%8F%E8%AE%AE%EF%BC%8C%E9%94%81%E7%B1%BB%E5%9E%8B%EF%BC%8C%E8%84%8F%E8%AF%BB%E3%80%81%E4%B8%8D%E5%8F%AF%E9%87%8D%E5%A4%8D%E8%AF%BB%E5%92%8C/

Continue reading 三级封锁协议两段锁以及隔离级别

Notepad++ 使用技巧

早就听说过Notepad++大名,最近用了下,觉得挺不错。

可以使用主题配色,看文本舒服多了。我最喜欢Rubyblue。

配合插件NppFTP: notepad++ ftp插件,查看服务端日志方便多了。修改文本文件自然不在话下。我想用这个修改服务器上的php文件岂不很好。

之前格式化个xml还要用专门的xml工具,使用nodepad++的xml tools插件,使用它的pretty print(line break)功能就能很好的格式化。

要格式化json或者javascript,可以使用jsmin插件中的jsformat功能。

常用快捷键:

Ctrl+L 删除行

Ctrl+D复制行

Ctrl+W关闭当前编辑页

Ctrl+Alt+鼠标 纵列选择

Ctrl+M批量修改文件

Continue reading Notepad++ 使用技巧

BCNF 示例

BCNF定义:

定义一:若关系模式R是第一范式,且每个属性都不传递依赖于R的候选键。这种关系模式就是BCNF模式。

定义二:若关系模式R∈1NF,且对于每一个非平凡的函数依赖X→Y,都有X 包含码,则R∈BCNF。

平凡函数依赖

当关系中属性集合Y是属性集合X的子集时(Y?X),存在函数依赖X→Y,即一组属性函数决定它的所有子集,这种函数依赖称为平凡函数依赖。

非平凡函数依赖

当关系中属性集合Y不是属性集合X的子集时,存在函数依赖X→Y,则称这种函数依赖为非平凡函数依赖。

第二个不好记,也不好理解,理解第一个就是了,两个是等同的。

举例 ①:

假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:

(仓库ID, 存储物品ID) →(管理员ID, 数量)

(管理员ID, 存储物品ID) → (仓库ID, 数量)

所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:

(仓库ID) → (管理员ID)

(管理员ID) → (仓库ID)

即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。也就是存在循环传递依赖(仓库ID) → (管理员ID)  → (仓库ID)

 

它会出现如下异常情况:

(1) 删除异常:

当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。

(2) 插入异常:

当仓库没有存储任何物品时,无法给仓库分配管理员。

(3) 更新异常:

如果仓库换了管理员,则表中所有行的管理员ID都要修改。

把仓库管理关系表分解为二个关系表:

仓库管理:StorehouseManage(仓库ID, 管理员ID);

仓库:Storehouse(仓库ID, 存储物品ID, 数量)。

这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。

举例②:

CSZ(CITY 城市,ST 街道,ZIP 邮编),其属性组上的函数依赖集是 F={( CITY,ST) →ZIP,ZIP→CITY}。

存在两个候选码:

(CITY, ST)->ZIP

(ST, ZIP)->CITY

(CITY, ST)和(ST, ZIP)是两个候选码,没有非主属性,自然CSZE∈3NF。

但存在传递依赖(CITY, ST)->ZIP->CITY, 所以CSZ∉BCNF。
关系模式CSZ 也存在种删除,插入,更新异常:

若将CSZ 分解为两个关系模式ZC(ZIP, CITY)和SZ(ST,ZIP),就不再有
非平凡的函数依赖的决定因素中不包含码的情况,都是BCNF 的关系模式了。

 

可以看到上面两个例子都是存在循环依赖造成的主属性传递依赖于码,不知道这是必然还是有别的例子属于3NF不属于BCNF但是也不存在循环依赖的情况?

Continue reading BCNF 示例

IO 接口,设备

IO这个词出现太多了太多了,这里整理一下思路。

计算机由控制单元,运算单元,存储单元,输入设备输出设备组成。其中IO指的就是输入输出。

关于IO又有接口,设备,操作的概念: 参见http://book.51cto.com/art/200704/45417.htm

IO接口

IO接口是实现外部设备与主机之间的连接和信息交换的设备,也可称I/O适配器(Adapter)或适配卡。目前有:

① 总线系统

② 直接内存访问(DMA)

③ 通道

④ SCSI(Small Computer System Interface)

⑤ 并行口

⑥ RS-232C接口

⑦ USB(Universal Serial Bus,通用串行总线)接口

⑧ IEEE 1394接口

IO接口的控制方式

(1)程序查询方式
这种方式下,CPU通过I/O指令询问指定外设当前的状态,如果外设准备就绪,则进行数据的输入或输出,否则CPU等待,循环查询。
这种方式的优点是结构简单,只需要少量的硬件电路即可,缺点是由于CPU的速度远远高于外设,因此通常处于等待状态,工作效率很低

(2)中断处理方式
在这种方式下,CPU不再被动等待,而是可以执行其他程序,一旦外设为数据交换准备就绪,可以向CPU提出服务请求,CPU如果响应该请求,便暂时停止当前程序的执行,转去执行与该请求对应的服务程序,完成后,再继续执行原来被中断的程序。
中断处理方式的优点是显而易见的,它不但为CPU省去了查询外设状态和等待外设就绪所花费的时间,提高了CPU的工作效率,还满足了外设的实时要求。但需要为每个I/O设备分配一个中断请求号和相应的中断服务程序,此外还需要一个中断控制器(I/O接口芯片)管理I/O设备提出的中断请求,例如设置中断屏蔽、中断请求优先级等。
此外,中断处理方式的缺点是每传送一个字符都要进行中断,启动中断控制器,还要保留和恢复现场以便能继续原程序的执行,花费的工作量很大,这样如果需要大量数据交换,系统的性能会很低。

(3)DMA(直接存储器存取)传送方式
DMA最明显的一个特点是它不是用软件而是采用一个专门的控制器来控制内存与外设之间的数据交流,无须CPU介入,大大提高CPU的工作效率。
在进行DMA数据传送之前,DMA控制器会向CPU申请总线控制 权,CPU如果允许,则将控制权交出,因此,在数据交换时,总线控制权由DMA控制器掌握,在传输结束后,DMA控制器将总线控制权交还给CPU。

(4) 通道方式

(5) 外围处理机(输入输出处理机)方式

有专门的处理机负责IO,一般用于大型系统

IO设备

键盘,鼠标,硬盘,打印机,扫描仪,网络设备等……

 

说实话,我还是没搞清楚!有时候把接口当成设备,有时又要把它当成控制方式,下面显然是控制方式:

常见I/O接口方式的分类方式有:

①按数据传送格式分类:串行接口和并行接口

②按时序控制方式分类:同步接口和异步接口

③按传送控制方式分类:直接程序传送接口,中断接口,DMA接口.

网络IO

还有网络IO这个说法,对于web方面的来说,性能与网络IO操作有很大关系,参见http://blog.csdn.net/historyasamirror/article/details/5778378

对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。

当一个read操作发生时,它会经历两个阶段:
1 等待数据准备 (Waiting for the data to be ready)
2 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。

阻塞IO:特点就是在IO执行的两个阶段都被block了,调用返回时数据一定是准备好了的。

非阻塞IO:调用马上返回,但是数据可能还没准备好,需要不断的主动询问kernel数据好了没有

多路IO(IO multiplexing):先选择,后获取。选择操作会阻塞知道其中一个有数据。

异步IO:调用马上返回,有数据后会发信号。(和ajax取数据有点像哈)

此文将多路分为同步IO,

这篇文章有另一种分法:http://blog.chinaunix.net/space.php?uid=20357359&do=blog&id=1963658

090830172605

将多路归类为异步阻塞,各有各道理,知道原理就好。不钻牛角尖!

显然非阻塞异步IO性能应该是最好的,Nodejs中的事件驱动非阻塞IO就是AIO。

Continue reading IO 接口,设备

【转】ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

 
转自:http://dongxuan.iteye.com/blog/901689

工作中多处接触到了ThreadPoolExecutor。趁着现在还算空,学习总结一下。

前记:

  1. jdk官方文档(javadoc)是学习的最好,最权威的参考。
  2. 文章分上中下。上篇中主要介绍ThreadPoolExecutor接受任务相关的两方面入参的意义和区别,池大小参数corePoolSize和maximumPoolSize,BlockingQueue选型(SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue);中篇中主要聊聊与keepAliveTime这个参数相关的话题;下片中介绍一下一些比较少用的该类的API,及他的近亲:ScheduledThreadPoolExecutor
  3. 如果理解错误,请直接指出。

查看JDK帮助文档,可以发现该类比较简单,继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口。

ThreadPoolExecutor的完整构造方法的签名是:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

先记着,后面慢慢解释。

===============================神奇分割线==================================

其实对于ThreadPoolExecutor的构造函数网上有N多的解释的,大多讲得都很好,不过我想先换个方式,从Executors这个类入手。因为他的几个构造工厂构造方法名字取得令人很容易了解有什么特点。但是其实Executors类的底层实现便是ThreadPoolExecutor!

ThreadPoolExecutor是Executors类的底层实现。

在JDK帮助文档中,有如此一段话:

“强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。”

可以推断出ThreadPoolExecutor与Executors类必然关系密切。

===============================神奇分割线==================================

OK,那就来看看源码吧,从newFixedThreadPool开始。

ExecutorService newFixedThreadPool(int nThreads):固定大小线程池。

可以看到,corePoolSize和maximumPoolSize的大小是一样的(实际上,后面会介绍,如果使用无界queue的话maximumPoolSize参数是没有意义的),keepAliveTime和unit的设值表名什么?-就是该实现不想keep alive!最后的BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。

Java代码 收藏代码

  1. public static ExecutorService newFixedThreadPool(int nThreads) { 
  2. return new ThreadPoolExecutor(nThreads, nThreads, 
  3.                                       0L, TimeUnit.MILLISECONDS, 
  4. new LinkedBlockingQueue<Runnable>()); 
  5.     } 

ExecutorService newSingleThreadExecutor():单线程。

可以看到,与fixedThreadPool很像,只不过fixedThreadPool中的入参直接退化为1

Java代码 收藏代码

  1. public static ExecutorService newSingleThreadExecutor() { 
  2. return new FinalizableDelegatedExecutorService 
  3.             (new ThreadPoolExecutor(1, 1, 
  4.                                     0L, TimeUnit.MILLISECONDS, 
  5. new LinkedBlockingQueue<Runnable>())); 
  6.     } 

ExecutorService newCachedThreadPool():无界线程池,可以进行自动线程回收。

这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。可能对于该BlockingQueue有些陌生,简单说:该QUEUE中,每个插入操作必须等待另一个

线程的对应移除操作。比如,我先添加一个元素,接下来如果继续想尝试添加则会阻塞,直到另一个线程取走一个元素,反之亦然。(想到什么?就是缓冲区为1的生产者消费者模式^_^)

注意到介绍中的自动回收线程的特性吗,为什么呢?先不说,但注意到该实现中corePoolSize和maximumPoolSize的大小不同。

Java代码 收藏代码

  1. public static ExecutorService newCachedThreadPool() { 
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 
  3.                                       60L, TimeUnit.SECONDS, 
  4. new SynchronousQueue<Runnable>()); 
  5.     } 

===============================神奇分割线==================================

到此如果有很多疑问,那是必然了(除非你也很了解了)

先从BlockingQueue<Runnable> workQueue这个入参开始说起。在JDK中,其实已经说得很清楚了,一共有三种类型的queue。以下为引用:(我会稍微修改一下,并用红色突出显示)

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
  • 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。(什么意思?如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)
  • 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程
  • 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

先不着急举例子,因为首先需要知道queue上的三种类型。

排队有三种通用策略:

  1. 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
  2. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
  3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

===============================神奇分割线==================================

到这里,该了解的理论已经够多了,可以调节的就是corePoolSize和maximumPoolSizes 这对参数还有就是BlockingQueue的选择。

例子一:使用直接提交策略,也即SynchronousQueue。

首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

我们使用一下参数构造ThreadPoolExecutor:

Java代码 收藏代码

  1. new ThreadPoolExecutor( 
  2. 2, 3, 30, TimeUnit.SECONDS,  
  3. new <span style="white-space: normal;">SynchronousQueue</span><Runnable>(),  
  4. new RecorderThreadFactory("CookieRecorderPool"),  
  5. new ThreadPoolExecutor.CallerRunsPolicy()); 

当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。
  2. 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。
  3. 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。
  4. 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中

例子二:使用无界队列策略,即LinkedBlockingQueue

这个就拿newFixedThreadPool来说,根据前文提到的规则:

写道

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。

那么当任务继续增加,会发生什么呢?

写道

如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

写道

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,如果任务内存大一些,不一会儿就爆了,呵呵。

可以仔细想想哈。

例子三:有界队列,使用ArrayBlockingQueue。

这个是最为复杂的使用,所以JDK不推荐使用也有些道理。与上面的相比,最大的特点便是可以防止资源耗尽的情况发生。

举例来说,请看如下构造方法:

Java代码 收藏代码

  1. new ThreadPoolExecutor( 
  2. 2, 4, 30, TimeUnit.SECONDS,  
  3. new ArrayBlockingQueue<Runnable>(2),  
  4. new RecorderThreadFactory("CookieRecorderPool"),  
  5. new ThreadPoolExecutor.CallerRunsPolicy()); 

假设,所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queu中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。

总结:

  1. ThreadPoolExecutor的使用还是很有技巧的。
  2. 使用无界queue可能会耗尽系统资源。
  3. 使用有界queue可能不能很好的满足性能,需要调节线程数和queue大小
  4. 线程数自然也有开销,所以需要根据不同应用进行调节。

通常来说对于静态任务可以归为:

  1. 数量大,但是执行时间很短
  2. 数量小,但是执行时间较长
  3. 数量又大执行时间又长
  4. 除了以上特点外,任务间还有些内在关系

看完这篇问文章后,希望能够可以选择合适的类型了

Continue reading 【转】ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别

Pagination


Total views.

© 2013 - 2019. All rights reserved.

Powered by Hydejack v6.6.1