xylz,imxylz

关注后端架构、中间件、分布式和并发编程

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  111 随笔 :: 10 文章 :: 2680 评论 :: 0 Trackbacks

2010年6月28日 #

     摘要: 世界邦旅行网创业团队(北京)招聘Java工程师/PHP工程师/测试工程师/前端工程师/移动开发工程师等  阅读全文
posted @ 2013-11-05 17:01 imxylz 阅读(13264) | 评论 (15)编辑 收藏

posted @ 2013-10-16 00:33 imxylz 阅读(9124) | 评论 (8)编辑 收藏

     摘要: Bash 为了提高命令的解析速度,将解析过的命令的全路径保存在hash表中,因此下次执行的时候就无需进行再次解析。如果在shell中修改了已经缓存过的命令路径,那么bash可能不能立即生效。这样就会发生命令不能解析或者文件不存在的问题,尽管可执行文件确实存在。  阅读全文
posted @ 2013-10-13 22:16 imxylz 阅读(3180) | 评论 (0)编辑 收藏

     摘要: OS X 下批量转换图片格式。  阅读全文
posted @ 2013-10-08 17:17 imxylz 阅读(5053) | 评论 (1)编辑 收藏

     摘要: Google的web字体在我朝访问巨慢,尤其是HTTPS方式更慢,本文帮助大家解决octopress默认的google web字体访问太慢的问题。  阅读全文
posted @ 2013-09-22 21:42 imxylz 阅读(3245) | 评论 (0)编辑 收藏

     摘要: JRebel最新版本6.0.0的下载地址及个人学习使用版本。  阅读全文
posted @ 2013-09-15 23:24 imxylz| 编辑 收藏

     摘要: 本文描述如何申请2.5$每年的SSL证书,并启用Nginx的HTTPS访问。  阅读全文
posted @ 2013-09-11 21:58 imxylz 阅读(5634) | 评论 (0)编辑 收藏

posted @ 2013-08-17 17:44 imxylz 阅读(3827) | 评论 (3)编辑 收藏

posted @ 2013-08-05 16:45 imxylz 阅读(29819) | 评论 (6)编辑 收藏

posted @ 2013-02-24 20:55 imxylz 阅读(5090) | 评论 (0)编辑 收藏

posted @ 2012-12-26 12:02 imxylz 阅读(17521) | 评论 (31)编辑 收藏

posted @ 2012-09-25 16:34 imxylz 阅读(28347) | 评论 (0)编辑 收藏

posted @ 2012-06-26 10:51 imxylz 阅读(2990) | 评论 (2)编辑 收藏

posted @ 2012-06-07 12:13 imxylz 阅读(4825) | 评论 (9)编辑 收藏

posted @ 2012-05-27 10:53 imxylz 阅读(3161) | 评论 (1)编辑 收藏

posted @ 2012-05-22 19:47 imxylz 阅读(3358) | 评论 (0)编辑 收藏

posted @ 2012-05-11 18:48 imxylz 阅读(9743) | 评论 (4)编辑 收藏

posted @ 2012-05-10 10:08 imxylz 阅读(10115) | 评论 (0)编辑 收藏

posted @ 2012-04-12 09:39 imxylz 阅读(5642) | 评论 (3)编辑 收藏

posted @ 2012-04-12 09:38 imxylz 阅读(3085) | 评论 (0)编辑 收藏

posted @ 2012-03-28 19:02 imxylz 阅读(28326) | 评论 (2)编辑 收藏

posted @ 2012-03-15 18:30 imxylz 阅读(11489) | 评论 (16)编辑 收藏

posted @ 2012-03-09 17:52 imxylz 阅读(8429) | 评论 (0)编辑 收藏

posted @ 2012-02-29 10:44 imxylz 阅读(2612) | 评论 (0)编辑 收藏

posted @ 2012-02-16 11:10 imxylz 阅读(6062) | 评论 (10)编辑 收藏

posted @ 2012-02-08 16:41 imxylz 阅读(4014) | 评论 (1)编辑 收藏

posted @ 2012-01-29 16:41 imxylz 阅读(7417) | 评论 (4)编辑 收藏

posted @ 2012-01-29 16:34 imxylz 阅读(3642) | 评论 (0)编辑 收藏

posted @ 2011-12-31 14:13 imxylz 阅读(7437) | 评论 (5)编辑 收藏

posted @ 2011-12-30 17:25 imxylz 阅读(6900) | 评论 (0)编辑 收藏

     摘要: 线程池

并发最常见用于线程池,显然使用线程池可以有效的提高吞吐量。
最常见、比较复杂一个场景是Web容器的线程池。Web容器使用线程池同步或者异步处理HTTP请求,同时这也可以有效的复用HTTP连接,降低资源申请的开销。通常我们认为HTTP请求时非常昂贵的,并且也是比较耗费资源和性能的,所以线程池在这里就扮演了非常重要的角色。
在线程池的章节中非常详细的讨论了线程池的原理和使用,同时也提到了,线程池的配置和参数对性能的影响是巨大的。不尽如此,受限于资源(机器的性能、网络的带宽等等)、依赖的服务,客户端的响应速度等,线程池的威力也不会一直增长。达到了线程池的瓶颈后,性能和吞吐量都会大幅度降低。
一直增加机器的性能或者增大线程的个数,并不一定能有效的提高吞吐量。高并发的情况下,机器的负载会大幅提升,这时候机器的稳定性、服务的可靠性都会下降。
尽管如此,线程池依然是提高吞吐量的一个有效措施,配合合适的参数能够有效的充分利用资源,提高资源的利用率。  阅读全文
posted @ 2011-12-29 16:31 imxylz 阅读(8111) | 评论 (0)编辑 收藏

     摘要: 死锁与活跃度

前面谈了很多并发的特性和工具,但是大部分都是和锁有关的。我们使用锁来保证线程安全,但是这也会引起一些问题。
锁顺序死锁(lock-ordering deadlock):多个线程试图通过不同的顺序获得多个相同的资源,则发生的循环锁依赖现象。
动态的锁顺序死锁(Dynamic Lock Order Deadlocks):多个线程通过传递不同的锁造成的锁顺序死锁问题。
资源死锁(Resource Deadlocks):线程间相互等待对方持有的锁,并且谁都不会释放自己持有的锁发生的死锁。也就是说当现场持有和等待的目标成为资源,就有可能发生此死锁。这和锁顺序死锁不一样的地方是,竞争的资源之间并没有严格先后顺序,仅仅是相互依赖而已。  阅读全文
posted @ 2011-12-29 14:04 imxylz 阅读(8203) | 评论 (2)编辑 收藏

     摘要: 刚看到这个月的编程语言排行榜,很显然java的霸主地位很快就会在发达国家被挤掉,C语言依然是王者(想想上个月自己买的两个C语言的书,冷汗直流)。看来我迟早要回归C,这才是真正的王道。



非常令人吃惊的是C++语言依然不够坚挺,由于Windows 7/Windows 8的发力,C#很快就会抢占C++的市场,估计很快就会将C++从前三名中挤下去。



iPhone/iPad的热销让Object C继续火热,前十的位置还是可以持续很久的,这一点毋庸置疑。移动设备开发的高端人才现在是高薪难求,如果有时间我也要继续关注下。  阅读全文
posted @ 2011-12-06 11:25 imxylz 阅读(4610) | 评论 (8)编辑 收藏

     摘要: Zookeeper客户端和服务端维持一个长连接,每隔10s向服务端发送一个心跳,服务端返回客户端一个响应。这就是一个Session连接,拥有全局唯一的session id。Session连接通常是一直有效,如果因为网络原因断开了连接,客户端会使用相同的session id进行重连。由于服务端保留了session的各种状态,尤其是各种瞬时节点是否删除依赖于session是否失效。
Session失效问题

通常客户端主动关闭连接认为是一次session失效。另外也有可能因为其它未知原因,例如网络超时导致的session失效问题。在服务端看来,无法区分session失效是何种情况,一次一旦发生session失效,一定时间后就会将session持有的所有watcher以及瞬时节点删除。
而对于Zookeeper客户端而言,一旦发生失效不知道是否该重连,这涉及到watcher和瞬时节点问题,因此Zookeeper客户端认为,一旦发生了seesion失效,那么就认为客户端死掉了。从而所有操作都不能够进行。参考 How should I handle SESSION  阅读全文
posted @ 2011-12-05 13:57 imxylz 阅读(28559) | 评论 (8)编辑 收藏

     摘要: 为了提高性能,最近将Redis从2.2.x的最新版2.2.12升级到2.4.x(2.4.2),惊喜的发现内存占用节省了很多。大赞!


有人说Redis的作者是一个勤奋的人,深表同意!


本来升级是为了增加批量操作从而提高性能,没想到内存占用节省了很多。

对于32位的操作系统而言,节省内存62%,对于64位操作系统而言节省73%。非常可观。  阅读全文
posted @ 2011-11-21 16:48 imxylz 阅读(3799) | 评论 (1)编辑 收藏

posted @ 2011-10-10 22:44 imxylz 阅读(1193) | 评论 (3)编辑 收藏

posted @ 2011-07-21 00:34 imxylz 阅读(14003) | 评论 (7)编辑 收藏

posted @ 2011-07-12 23:15 imxylz 阅读(17374) | 评论 (3)编辑 收藏

posted @ 2011-06-17 09:25 imxylz 阅读(5173) | 评论 (3)编辑 收藏

posted @ 2011-06-12 00:24 imxylz 阅读(11232) | 评论 (36)编辑 收藏

     摘要: 当Ajax遇到GBK/GB2312,我们恨不能将所有项目的编码统一改为UTF-8。显然这个做法,成本太高了。下面给出了一个完善的解决方案。此方案不仅仅限于Ajax提交数据,也适用于普通访问请求。  阅读全文
posted @ 2011-06-10 23:46 imxylz 阅读(8708) | 评论 (6)编辑 收藏

posted @ 2011-04-12 14:02 imxylz 阅读(3350) | 评论 (0)编辑 收藏

     摘要: 本节中将探讨线程池是如何处理任务结果以及延迟、周期性任务调度是如何实现的。
结合上节线程池的原理和实现,彻底分析了线程池对任务的结果处理,尤其是对延迟、周期性任务是如何执行的。
这也是并发系列中源码分析的最后一小节。  阅读全文
posted @ 2011-02-13 20:21 imxylz 阅读(11259) | 评论 (6)编辑 收藏

     摘要: 这一节中我们将详细讨论线程池是如何执行一个任务的,尤其是如何处理线程池的大小、核心大小、最大大小、任务队列等之间的关系的。  阅读全文
posted @ 2011-02-11 23:48 imxylz 阅读(11644) | 评论 (2)编辑 收藏

     摘要: 我们根据线程池的要求也很能够猜测出其数据结构出来。
线程池需要支持多个线程并发执行,因此有一个线程集合Collection来执行线程任务;
涉及任务的异步执行,因此需要有一个集合来缓存任务队列Collection
很显然在多个线程之间协调多个任务,那么就需要一个线程安全的任务集合,同时还需要支持阻塞、超时操作,那么BlockingQueue是必不可少的;
既然是线程池,出发点就是提高系统性能同时降低资源消耗,那么线程池的大小就有限制,因此需要有一个核心线程池大小(线程个数)和一个最大线程池大小(线程个数),有一个计数用来描述当前线程池大小;
如果是有限的线程池大小,那么长时间不使用的线程资源就应该销毁掉,这样就需要一个线程空闲时间的计数来描述线程何时被销毁;
前面描述过线程池也是有生命周期的,因此需要有一个状态来描述线程池当前的运行状态;
线程池的任务队列如果有边界,那么就需要有一个任务拒绝策略来处理过多的任务,同时在线程池的销毁阶段也需要有一个任务拒绝策略来处理新加入的任务;
上面种  阅读全文
posted @ 2011-01-18 23:43 imxylz 阅读(16054) | 评论 (6)编辑 收藏

     摘要: 在JDK 5.0之前,java.util.Timer/TimerTask是唯一的内置任务调度方法,而且在很长一段时间里很热衷于使用这种方式进行周期性任务调度。
首先研究下Timer/TimerTask的特性(至于javax.swing.Timer就不再研究了)。
上面三段代码反映了Timer/TimerTask的以下特性:
Timer对任务的调度是基于绝对时间的。
所有的TimerTask只有一个线程TimerThread来执行,因此同一时刻只有一个TimerTask在执行。
任何一个TimerTask的执行异常都会导致Timer终止所有任务。
由于基于绝对时间并且是单线程执行,因此在多个任务调度时,长时间执行的任务被执行后有可能导致短时间任务快速在短时间内被执行多次或者干脆丢弃多个任务。  阅读全文
posted @ 2011-01-10 23:39 imxylz 阅读(14430) | 评论 (32)编辑 收藏

     摘要: 上一节中提到关闭线程池过程中需要对新提交的任务进行处理。这个是java.util.concurrent.RejectedExecutionHandler处理的逻辑。

在没有分析线程池原理之前先来分析下为什么有任务拒绝的情况发生。
这里先假设一个前提:线程池有一个任务队列,用于缓存所有待处理的任务,正在处理的任务将从任务队列中移除。因此在任务队列长度有限的情况下就会出现新任务的拒绝处理问题,需要有一种策略来处理应该加入任务队列却因为队列已满无法加入的情况。另外在线程池关闭的时候也需要对任务加入队列操作进行额外的协调处理。

RejectedExecutionHandler提供了四种方式来处理任务拒绝策略。  阅读全文
posted @ 2011-01-08 22:47 imxylz 阅读(9958) | 评论 (0)编辑 收藏

     摘要: 本着开发的原则,既然用到了别人家的东西,所以决定公开出来,也算是给别人一个参考。  阅读全文
posted @ 2011-01-05 10:00 imxylz 阅读(4568) | 评论 (1)编辑 收藏

     摘要:
我们知道线程是有多种执行状态的,同样管理线程的线程池也有多种状态。JVM会在所有线程(非后台daemon线程)全部终止后才退出,为了节省资源和有效释放资源关闭一个线程池就显得很重要。有时候无法正确的关闭线程池,将会阻止JVM的结束。
线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行。因此关闭线程池可能出现一下几种情况:
平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务
立即关闭:取消所有正在执行和未执行的任务
另外关闭线程池后对于任务的状态应该有相应的反馈信息。  阅读全文
posted @ 2011-01-04 22:54 imxylz 阅读(12555) | 评论 (6)编辑 收藏

     摘要: Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
下面这张图完整描述了线程池的类体系结构。  阅读全文
posted @ 2010-12-21 23:32 imxylz 阅读(13744) | 评论 (4)编辑 收藏

     摘要: 从这一节开始正式进入线程池的部分。其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充、完善。
其实线程池是并发包里面很重要的一部分,在实际情况中也是使用很多的一个重要组件。
下图描述的是线程池API的一部分。广义上的完整线程池可能还包括Thread/Runnable、Timer/TimerTask等部分。这里只介绍主要的和高级的API以及架构和原理。  阅读全文
posted @ 2010-12-19 13:24 imxylz 阅读(11941) | 评论 (5)编辑 收藏

     摘要: 最近的项目使用的是旧的ibatis2.x版本,有时候为了方便调试,想输出SQL执行的语句和参数。我记得应该有某些logger的日志级别修改为DEBUG就可以看到。当然为了方便可以直接在log4j(如果使用log4j的话)的root日志级别修改为DEBUG,并且输出appender的接受级别修改为DEBUG就可以了。这样是可以看到日志信息(SQL/参数)等,但是同时也输出了过多的其它logger信息,显然在一个稍微大一点的系统里面debug的信息应该都是非常多的,不说别的,光是spring的日志就够好多页了。
为了解决过多的日志,翻出ibatis源码,看了下。ibatis的执行流程大致是这样的。  阅读全文
posted @ 2010-12-05 15:17 imxylz 阅读(3517) | 评论 (3)编辑 收藏

posted @ 2010-12-03 16:13 imxylz 阅读(10020) | 评论 (7)编辑 收藏

posted @ 2010-11-24 14:34 imxylz 阅读(6261) | 评论 (16)编辑 收藏

     摘要: 本小节是《并发容器》的最后一部分,这一个小节描述的是针对List/Set接口的一个线程版本。
在《并发队列与Queue简介》中介绍了并发容器的一个概括,主要描述的是Queue的实现。其中特别提到一点LinkedList是List/Queue的实现,但是LinkedList确实非线程安全的。不管BlockingQueue还是ConcurrentMap的实现,我们发现都是针对链表的实现,当然尽可能的使用CAS或者Lock的特性,同时都有通过锁部分容器来提供并发的特性。而对于List或者Set而言,增、删操作其实都是针对整个容器,因此每次操作都不可避免的需要锁定整个容器空间,性能肯定会大打折扣。要实现一个线程安全的List/Set,只需要在修改操作的时候进行同步即可,比如使用java.util.Collections.synchronizedList(List)或者java.util.Collections.synchronizedSet(Set)。当然也可以使用Lock来实现线程安全的List/Set。
通常情况下我们的高并发都发生在“多读少写”的情况,因此如果  阅读全文
posted @ 2010-11-23 22:22 imxylz 阅读(14672) | 评论 (1)编辑 收藏

     摘要: 可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。Exchanger 可能被视为 SynchronousQueue 的双向形式。
换句话说Exchanger提供的是一个交换服务,允许原子性的交换两个(多个)对象,但同时只有一对才会成功。先看一个简单的实例模型。  阅读全文
posted @ 2010-11-22 22:31 imxylz 阅读(7724) | 评论 (0)编辑 收藏

posted @ 2010-11-02 16:09 imxylz 阅读(5754) | 评论 (19)编辑 收藏

     摘要: 这个小节介绍Queue的最后一个工具,也是最强大的一个工具。从名称上就可以看到此工具的特点:双向并发阻塞队列。所谓双向是指可以从队列的头和尾同时操作,并发只是线程安全的实现,阻塞允许在入队出队不满足条件时挂起线程,这里说的队列是指支持FIFO/FILO实现的链表。

首先看下LinkedBlockingDeque的数据结构。通常情况下从数据结构上就能看出这种实现的优缺点,这样就知道如何更好的使用工具了。  阅读全文
posted @ 2010-08-18 16:01 imxylz 阅读(9725) | 评论 (5)编辑 收藏

posted @ 2010-08-12 00:54 imxylz 阅读(3115) | 评论 (14)编辑 收藏

     摘要:
有一段时间没有更新了。接着上节继续吧。
Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque。这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂。下图描述的是Deque的完整体系图。需要说明的是LinkedList也已经加入了Deque的一部分(LinkedList是从jdk1.2 开始就存在数据结构)。  阅读全文
posted @ 2010-08-12 00:13 imxylz 阅读(13586) | 评论 (4)编辑 收藏

     摘要:
在Set中有一个排序的集合SortedSet,用来保存按照自然顺序排列的对象。Queue中同样引入了一个支持排序的FIFO模型。
并发队列与Queue简介 中介绍了,PriorityQueue和PriorityBlockingQueue就是支持排序的Queue。显然一个支持阻塞的排序Queue要比一个非线程安全的Queue实现起来要复杂的多,因此下面只介绍PriorityBlockingQueue,至于PriorityQueue只需要去掉Blocking功能就基本相同了。  阅读全文
posted @ 2010-07-30 16:15 imxylz 阅读(14111) | 评论 (0)编辑 收藏

     摘要: 在上一节中详细分析了LinkedBlockingQueue 的实现原理。实现一个可扩展的队列通常有两种方式:一种方式就像LinkedBlockingQueue一样使用链表,也就是每一个元素带有下一个元素的引用,这样的队列原生就是可扩展的;另外一种就是通过数组实现,一旦队列的大小达到数组的容量的时候就将数组扩充一倍(或者一定的系数倍),从而达到扩容的目的。常见的ArrayList就属于第二种。前面章节介绍过的HashMap确是综合使用了这两种方式。
对于一个Queue而言,同样可以使用数组实现。使用数组的好处在于各个元素之间原生就是通过数组的索引关联起来的,一次元素之间就是有序的,在通过索引操作数组就方便多了。当然也有它不利的一面,扩容起来比较麻烦,同时删除一个元素也比较低效。
ArrayBlockingQueue 就是Queue的一种数组实现。  阅读全文
posted @ 2010-07-27 22:04 imxylz 阅读(12913) | 评论 (0)编辑 收藏

     摘要: 在《并发容器 part 4 并发队列与Queue简介》节中的类图中可以看到,对于Queue来说,BlockingQueue是主要的线程安全版本。这是一个可阻塞的版本,也就是允许添加/删除元素被阻塞,直到成功为止。
BlockingQueue相对于Queue而言增加了两个操作:put/take。下面是一张整理的表格。  阅读全文
posted @ 2010-07-24 00:02 imxylz 阅读(19605) | 评论 (6)编辑 收藏

     摘要: ConcurrentLinkedQueue是Queue的一个线程安全实现。先来看一段文档说明。
一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。

由于ConcurrentLinkedQueue只是简单的实现了一个队列Queue,因此从API的角度讲,没有多少值的介绍,使用起来也很简单,和前面遇到的所有FIFO队列都类似。出队列只能操作头节点,入队列只能操作尾节点,任意节点操作就需要遍历完整的队列。
重点放在解释ConcurrentLinkedQueue的原理和实现上。  阅读全文
posted @ 2010-07-23 14:11 imxylz 阅读(19986) | 评论 (2)编辑 收藏

     摘要: Queue是JDK 5以后引入的新的集合类,它属于Java Collections Framework的成员,在Collection集合中和List/Set是同一级别的接口。通常来讲Queue描述的是一种FIFO的队列,当然不全都是,比如PriorityQueue是按照优先级的顺序(或者说是自然顺序,借助于Comparator接口)。
下图描述了Java Collections Framework中Queue的整个家族体系。
对于Queue而言是在Collection的基础上增加了offer/remove/poll/element/peek方法,另外重新定义了add方法。对于这六个方法,有不同的定义。  阅读全文
posted @ 2010-07-21 12:21 imxylz 阅读(21426) | 评论 (5)编辑 收藏

     摘要: 在上一篇中介绍了HashMap的原理,这一节是ConcurrentMap的最后一节,所以会完整的介绍ConcurrentHashMap的实现。

ConcurrentHashMap原理

在读写锁章节部分介绍过一种是用读写锁实现Map的方法。此种方法看起来可以实现Map响应的功能,而且吞吐量也应该不错。但是通过前面对读写锁原理的分析后知道,读写锁的适合场景是读操作>>写操作,也就是读操作应该占据大部分操作,另外读写锁存在一个很严重的问题是读写操作不能同时发生。要想解决读写同时进行问题(至少不同元素的读写分离),那么就只能将锁拆分,不同的元素拥有不同的锁,这种技术就是“锁分离”技术。
默认情况下ConcurrentHashMap是用了16个类似HashMap 的结构,其中每一个HashMap拥有一个独占锁。也就是说最终的效果就是通过某种Hash算法,将任何一个元素均匀的映射到某个HashMap的Map.Entry上面,而对某个一个元素的操作就集中在其分布的HashMap上,与其它HashMap无关。这样就支持最多16个并发的写操作。  阅读全文
posted @ 2010-07-20 17:48 imxylz 阅读(20978) | 评论 (9)编辑 收藏

     摘要: 本来想比较全面和深入的谈谈ConcurrentHashMap的,发现网上有很多对HashMap和ConcurrentHashMap分析的文章,因此本小节尽可能的分析其中的细节,少一点理论的东西,多谈谈内部设计的原理和思想。
要谈ConcurrentHashMap的构造,就不得不谈HashMap的构造,因此先从HashMap开始简单介绍。

HashMap原理
我们从头开始设想。要将对象存放在一起,如何设计这个容器。目前只有两条路可以走,一种是采用分格技术,每一个对象存放于一个格子中,这样通过对格子的编号就能取到或者遍历对象;另一种技术就是采用串联的方式,将各个对象串联起来,这需要各个对象至少带有下一个对象的索引(或者指针)。显然第一种就是数组的概念,第二种就是链表的概念。所有的容器的实现其实都是基于这两种方式的,不管是数组还是链表,或者二者俱有。HashMap采用的就是数组的方式。
有了存取对象的容器后还需要以下两个条件才能完成Map所需要的条件。  阅读全文
posted @ 2010-07-20 00:22 imxylz 阅读(22880) | 评论 (3)编辑 收藏

     摘要: 从这一节开始正式进入并发容器的部分,来看看JDK 6带来了哪些并发容器。
在JDK 1.4以下只有Vector和Hashtable是线程安全的集合(也称并发容器,Collections.synchronized*系列也可以看作是线程安全的实现)。从JDK 5开始增加了线程安全的Map接口ConcurrentMap和线程安全的队列BlockingQueue(尽管Queue也是同时期引入的新的集合,但是规范并没有规定一定是线程安全的,事实上一些实现也不是线程安全的,比如PriorityQueue、ArrayDeque、LinkedList等,在Queue章节中会具体讨论这些队列的结构图和实现)。

在介绍ConcurrencyMap之前先来回顾下Map的体系结构。下图描述了Map的体系结构,其中蓝色字体的是JDK 5以后新增的并发容器。  阅读全文
posted @ 2010-07-19 15:25 imxylz 阅读(24595) | 评论 (8)编辑 收藏

posted @ 2010-07-16 11:59 imxylz 阅读(9021) | 评论 (4)编辑 收藏

     摘要:
主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是《Java Concurrency in Practice》,结合自己的理解和实际应用对锁机制进行一个小小的总结。

首先需要强调的一点是:所有锁(包括内置锁和高级锁)都是有性能消耗的,也就是说在高并发的情况下,由于锁机制带来的上下文切换、资源同步等消耗是非常可观的。在某些极端情况下,线程在锁上的消耗可能比线程本身的消耗还要多。所以如果可能的话,在任何情况下都尽量少用锁,如果不可避免那么采用非阻塞算法是一个不错的解决方案,但是却也不是绝对的。  阅读全文
posted @ 2010-07-16 00:15 imxylz 阅读(16557) | 评论 (2)编辑 收藏

     摘要:
这一节主要是谈谈读写锁的实现。
上一节中提到,ReadWriteLock看起来有两个锁:readLock/writeLock。如果真的是两个锁的话,它们之间又是如何相互影响的呢?
事实上在ReentrantReadWriteLock里锁的实现是靠java.util.concurrent.locks.ReentrantReadWriteLock.Sync完成的。这个类看起来比较眼熟,实际上它是AQS的一个子类,这中类似的结构在CountDownLatch、ReentrantLock、Semaphore里面都存在。同样它也有两种实现:公平锁和非公平锁,也就是java.util.concurrent.locks.ReentrantReadWriteLock.FairSync和java.util.concurrent.locks.ReentrantReadWriteLock.NonfairSync。这里暂且不提。
在ReentrantReadWriteLock里面的锁主体就是一个Sync,也就是上面提到的FairSync或者NonfairSync,所以说实际  阅读全文
posted @ 2010-07-15 00:41 imxylz 阅读(20295) | 评论 (1)编辑 收藏

     摘要: 从这一节开始介绍锁里面的最后一个工具:读写锁(ReadWriteLock)。
ReentrantLock 实现了标准的互斥操作,也就是一次只能有一个线程持有锁,也即所谓独占锁的概念。前面的章节中一直在强调这个特点。显然这个特点在一定程度上面减低了吞吐量,实际上独占锁是一种保守的锁策略,在这种情况下任何“读/读”,“写/读”,“写/写”操作都不能同时发生。但是同样需要强调的一个概念是,锁是有一定的开销的,当并发比较大的时候,锁的开销就比较客观了。所以如果可能的话就尽量少用锁,非要用锁的话就尝试看能否改造为读写锁。
ReadWriteLock描述的是:一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。也就是说读写锁使用的场合是一个共享资源被大量读取操作,而只有少量的写操作(修改数据)。清单1描述了ReadWriteLock的API。  阅读全文
posted @ 2010-07-14 14:18 imxylz 阅读(24224) | 评论 (4)编辑 收藏

     摘要: Semaphore 是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
说白了,Semaphore是一个计数器,在计数器不为0的时候对线程就放行,一旦达到0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少1,同样每次释放一个许可都会导致计数器增加1,一旦达到了0,新的许可请求线程将被挂起。
缓存池整好使用此思想来实现的,比如链接池、对象池等。  阅读全文
posted @ 2010-07-13 22:41 imxylz 阅读(23350) | 评论 (2)编辑 收藏

     摘要:
如果说CountDownLatch是一次性的,那么CyclicBarrier正好可以循环使用。它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。所谓屏障点就是一组任务执行完毕的时刻。
  阅读全文
posted @ 2010-07-12 23:33 imxylz 阅读(22361) | 评论 (2)编辑 收藏

     摘要: 此小节介绍几个与锁有关的有用工具。
闭锁(Latch)
闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。CountDownLatch有一个正数计数器,countDown方法对计数器做减操作,await方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。  阅读全文
posted @ 2010-07-09 09:21 imxylz 阅读(29311) | 评论 (6)编辑 收藏

     摘要: 这是一份完整的Java 并发整理笔记,记录了我最近几年学习Java并发的一些心得和体会。  阅读全文
posted @ 2010-07-08 19:17 imxylz 阅读(167886) | 评论 (43)编辑 收藏

     摘要: 本小节介绍锁释放Lock.unlock()。
Release/TryRelease
unlock操作实际上就调用了AQS的release操作,释放持有的锁。  阅读全文
posted @ 2010-07-08 12:33 imxylz 阅读(30456) | 评论 (11)编辑 收藏

     摘要: 接上篇,这篇从Lock.lock/unlock开始。特别说明在没有特殊情况下所有程序、API、文档都是基于JDK 6.0的。
在没有深入了解内部机制及实现之前,先了解下为什么会存在公平锁和非公平锁。公平锁保证一个阻塞的线程最终能够获得锁,因为是有序的,所以总是可以按照请求的顺序获得锁。不公平锁意味着后请求锁的线程可能在其前面排列的休眠线程恢复前拿到锁,这样就有可能提高并发的性能。这是因为通常情况下挂起的线程重新开始与它真正开始运行,二者之间会产生严重的延时。因此非公平锁就可以利用这段时间完成操作。这是非公平锁在某些时候比公平锁性能要好的原因之一。  阅读全文
posted @ 2010-07-07 00:05 imxylz 阅读(40132) | 评论 (6)编辑 收藏

     摘要: 在理解J.U.C原理以及锁机制之前,我们来介绍J.U.C框架最核心也是最复杂的一个基础类:java.util.concurrent.locks.AbstractQueuedSynchronizer。

AQS
AbstractQueuedSynchronizer,简称AQS,是J.U.C最复杂的一个类,导致绝大多数讲解并发原理或者实战的时候都不会提到此类。但是虚心的作者愿意借助自己有限的能力和精力来探讨一二(参考资源中也有一些作者做了部分的分析。)。
首先从理论知识开始,在了解了相关原理后会针对源码进行一些分析,最后加上一些实战来描述。  阅读全文
posted @ 2010-07-06 18:29 imxylz 阅读(53003) | 评论 (3)编辑 收藏

     摘要: 前面的章节主要谈谈原子操作,至于与原子操作一些相关的问题或者说陷阱就放到最后的总结篇来整体说明。从这一章开始花少量的篇幅谈谈锁机制。
上一个章节中谈到了锁机制,并且针对于原子操作谈了一些相关的概念和设计思想。接下来的文章中,尽可能的深入研究锁机制,并且理解里面的原理和实际应用场合。
尽管synchronized在语法上已经足够简单了,在JDK 5之前只能借助此实现,但是由于是独占锁,性能却不高,因此JDK 5以后就开始借助于JNI来完成更高级的锁实现。
JDK 5中的锁是接口java.util.concurrent.locks.Lock。另外java.util.concurrent.locks.ReadWriteLock提供了一对可供读写并发的锁。根据前面的规则,我们从java.util.concurrent.locks.Lock的API开始。  阅读全文
posted @ 2010-07-05 13:37 imxylz 阅读(42188) | 评论 (11)编辑 收藏

     摘要: 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁)。
锁机制存在以下问题:
(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。
(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。
独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。  阅读全文
posted @ 2010-07-04 18:03 imxylz 阅读(47971) | 评论 (19)编辑 收藏

     摘要: 在这个小结里面重点讨论原子操作的原理和设计思想。
由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念。
在Java Concurrency in Practice中是这样定义线程安全的:
当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。  阅读全文
posted @ 2010-07-03 20:40 imxylz 阅读(46527) | 评论 (16)编辑 收藏

     摘要: 在这一部分开始讨论数组原子操作和一些其他的原子操作。
AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray的API类似,选择有代表性的AtomicIntegerArray来描述这些问题。
int get(int i)
获取位置 i 的当前值。很显然,由于这个是数组操作,就有索引越界的问题(IndexOutOfBoundsException异常)。

对于下面的API起始和AtomicInteger是类似的,这种通过方法、参数的名称就能够得到函数意义的写法是非常值得称赞的。在《重构:改善既有代码的设计》和《代码整洁之道》中都非常推崇这种做法。  阅读全文
posted @ 2010-07-02 14:19 imxylz 阅读(48107) | 评论 (6)编辑 收藏

     摘要: 从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始)。很多情况下我们只是需要一个简单的、高效的、线程安全的递增递减方案。注意,这里有三个条件:简单,意味着程序员尽可能少的操作底层或者实现起来要比较容易;高效意味着耗用资源要少,程序处理速度要快;线程安全也非常重要,这个在多线程下能保证数据的正确性。这三个条件看起来比较简单,但是实现起来却难以令人满意。
通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:或者变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三个操作时“原子性”的。  阅读全文
posted @ 2010-07-01 15:21 imxylz 阅读(65798) | 评论 (2)编辑 收藏

     摘要: 去年年底有一个Guice的研究计划,可惜由于工作“繁忙”加上实际工作中没有用上导致“无疾而终”,最终只是完成了Guice的初步学习教程,深入的研究没有继续进行下去。
最近一直用的比较多的就是java.util.concurrent(J.U.C),实际上这块一直也没有完全深入研究,这次准备花点时间研究下Java里面整个并发体系。初步的设想包括比较大的方便(包括硬件、软件、思想以及误区等等),因此可能会持续较长的时间。这块内容也是Java在多线程方面引以为豪的一部分,深入这一部分不仅对整个Java体系有更深的了解,也对工作、学习的态度有多帮助。  阅读全文
posted @ 2010-06-30 18:09 imxylz 阅读(69228) | 评论 (8)编辑 收藏


©2009-2014 IMXYLZ