‘ 本站原创 ’ 目录归档
华东地区scala爱好者聚会(2014上海)
感谢看处方的汪院长和烨明,为这次活动提供了场地和零食,去年在上海的scala聚会也是看处方的汪院长发起的。这次聚会人比上次略微多一些了,而且整体水平是不断提升的。
聚石分享了来往使用scala构建通讯的一些经验,来往的IM部分采用自己的协议(有些类似spdy,要简单很多),在协议网关的实现上主要使用scala、akka,他主要分享了这个过程中踩过的一些坑。
诺铁的分享是scala集合部分,这部分内容是小中见大的,有部分内容来自《scala in depth》(顺便提一下他翻译的这本书快要出版了)。整个ppt很清晰,把Vector
的细节讲的比较透,另外还提到Set的一个细节是继承自(T)=>Boolean
这个函数类型,也就是contains
方法,比如Set(1,2,3)(1)
将返回true,这种设计确实怪异,容易误解。 阅读全文
JVM的重排序
感谢同事【沐剑】的投稿
重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。
在并发程序中,程序员会特别关注不同进程或线程之间的数据同步,特别是多个线程同时修改同一变量时,必须采取可靠的同步或其它措施保障数据被正确地修改,这里的一条重要原则是:不要假设指令执行的顺序,你无法预知不同线程之间的指令会以何种顺序执行。
但是在单线程程序中,通常我们容易假设指令是顺序执行的,否则可以想象程序会发生什么可怕的变化。理想的模型是:各种指令执行的顺序是唯一且有序的,这个顺序就是它们被编写在代码中的顺序,与处理器或其它因素无关,这种模型被称作顺序一致性模型,也是基于冯·诺依曼体系的模型。当然,这种假设本身是合理的,在实践中也鲜有异常发生,但事实上,没有哪个现代多处理器架构会采用这种模型,因为它是在是太低效了。而在编译优化和CPU流水线中,几乎都涉及到指令重排序。 阅读全文
Java SE 8 在并发工具方面的加强
本文首发于InfoQ。
Java 8在Lambda表达式、接口默认方式、新的日期API等方面引入的新特性广受关注,同时在并发编程方面也做出了大量改进。以往的几个Java版本都对java.util.concurrent做了不同程度的增强,比如Java 7的Fork/Join框架,而Java 8则进一步在java.util.concurrent下增加了新的接口、类与方法。目前java.util.concurrent的官方文档已经更新,变更部分总结如下: 阅读全文
Bug:LinkedTransferQueue的数据暂失和CPU爆满以及修复
一个因中断或者超时的调用可能会引起数据丢失和CPU爆满。
前几天读LinkedTransferQueue(以下简称ltq)的源码,想加深下对松弛型双重队列的理解,无意中发现了这个问题:),经过仔细检查后确认了这是个bug,存在于JDK1.7.0_40和刚发布的JDK8中,去google和oracle官方似乎也没有搜索到这个问题。
重现bug:先来重现下这个bug,由于对并发线程的执行顺序预先不能做任何假设,所以很可能根本就不存在所谓的重现错误的“测试用例”,或者说这个测试用例应该是某种“执行顺序”。所以我一开始的做法是copy了一份ltq的源码,通过某个地方加自旋…但是这种方法毕竟要修改源码,后来我发现直接debug进源码就可以轻易重现bug了。 阅读全文
上下文切换与多处理器
对于进程有两个幻觉:一认为自己独享内存;二以为自己独享处理器。我们对于一台机器上的多个进程的幻觉是感觉他们是同时运行。
我们来依次解释下上面的三个幻觉:
关于独享内存不是我们的重点,简单说说。独享内存是指我们每个进程都独享虚拟内存。而虚拟内存地址最终是通过MMU翻译成实际的物理地址。这样做只是为了提供一种逻辑上的连续性,屏蔽内存碎片或是规避因内存有限而扩展到硬盘的各种问题,这样不用考虑实际的的限制从而使应用程序开发变得容易。还有一个值得注意的问题是在这个虚拟内存中如果这个进程是多线程的,那么将共享改空间,除了各自的堆栈、寄存器和所谓的虚拟处理器。这样会导致一个问题就是多个线程的stacksize对进程栈空间的要求呈线性增长,与复杂的多层级递归运算类似,导致stackoverflow。这也是好多语言比如Java的线程模型要求线程创建时指定好stacksize大小的原因。
如何合理地估算线程池大小?
感谢网友【蒋小强】投稿。
如何合理地估算线程池大小?
这个问题虽然看起来很小,却并不那么容易回答。大家如果有更好的方法欢迎赐教,先来一个天真的估算方法:假设要求一个系统的TPS(Transaction Per Second或者Task Per Second)至少为20,然后假设每个Transaction由一个线程完成,继续假设平均每个线程处理一个Transaction的时间为4s。那么问题转化为:
如何设计线程池大小,使得可以在1s内处理完20个Transaction?
计算过程很简单,每个线程的处理能力为0.25TPS,那么要达到20TPS,显然需要20/0.25=80个线程。
很显然这个估算方法很天真,因为它没有考虑到CPU数目。一般服务器的CPU核数为16或者32,如果有80个线程,那么肯定会带来太多不必要的线程上下文切换开销。
阅读全文
Tomcat进程意外退出的问题分析
感谢同事宏江投递本稿。
节前某个部门的测试环境反馈tomcat会意外退出,我们到实际环境排查后发现不是jvm crash,日志里有进程销毁的记录,从pause到destory的整个过程:
org.apache.coyote.AbstractProtocol pause
Pausing ProtocolHandler
org.apache.catalina.core.StandardService stopInternal
Stopping service Catalina
org.apache.coyote.AbstractProtocol stop
Stopping ProtocolHandler
org.apache.coyote.AbstractProtocol destroy
Destroying ProtocolHandler
不正当使用HashMap导致cpu 100%的问题追究
因最近hashmap误用引起的死循环又发生了一些案例,左耳朵浩子写了一篇blog 疫苗:Java HashMap的死循环,看了一下,大家的分析如出一辙。这篇blog也是好几年前写的了,之前在平台技术部的博客上贴过,随着组织结构的调整,那个博客可能不再维护,把这篇文章在这儿也保存一下。
李鹏同学在blog里写了篇关于HashMap死锁模拟的文章: http://blog.csdn.net/madding/archive/2010/08/25/5838477.aspx 做个纠正,那个不是死锁问题,而是死循环。
这个问题,我们以前讨论过。 校长之前的博客和淘宝的毕玄的《分布式Java应用:基础与实践》一书中都提到过 velocity导致cpu 100% 的bug,起因是HashMap的使用不当所致。
深入剖析ConcurrentHashMap(2)
经过之前的铺垫,现在可以进入正题了。
我们关注的操作有:get,put,remove 这3个操作。
对于哈希表,Java中采用链表的方式来解决hash冲突的。
一个HashMap的数据结构看起来类似下图:
实现了同步的HashTable也是这样的结构,它的同步使用锁来保证的,并且所有同步操作使用的是同一个锁对象。这样若有n个线程同时在get时,这n个线程要串行的等待来获取锁。
深入剖析ConcurrentHashMap(1)
原文是09年时写的,在公司的邮件列表发过,同事一粟 和清英 创建的并发编程网 对这方面概念和实战有更好的文章,贴出来仅供参考。pdf格式在:http://www.slideshare.net/hongjiang/concurrent-hashmap 可以获取
ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable。对于ConcurrentHashMap是如何提高其效率的,可能大多人只是知道它使用了多个锁代替HashTable中的单个锁,也就是锁分离技术(Lock Stripping)。实际上,ConcurrentHashMap对提高并发方面的优化,还有一些其它的技巧在里面(比如你是否知道在get操作的时候,它是否也使用了锁来保护?)。
我会试图用通俗一点的方法讲解一下 ConcurrentHashMap的实现方式,不过因为水平有限,在整理这篇文档的过程中,发现了更多自己未曾深入思考过的地方,使得我不得不从新调整了自己的讲解方式。我假设受众者大多是对Java存储模型(JMM)认识并不很深的(我本人也是)。如果我们不断的对ConcurrentHashMap中一些实现追问下去,最终还是要归到JMM层面甚至更底层的。这篇文章的关注点主要在同步方面,并不去分析HashMap中的一些数据结构方面的实现。
话说模式匹配(8) 一个抽取器的例子
一个抽取器的例子
目前List的序列模式(sequence pattern)可以支持对前边若干元素的匹配,比如:List(1,2,3,_*)
,如果想要实现 List(_*, lastEle)
这样的形式,就需要通过自定义一个抽取器来实现了
// 自定义Extractor
object Append {
// 接受List结构
def unapply[A] (l: List[A]) = {
// 返回Tuple2:前边的若干元素和最后一个元素
Some( (l.init, l.last) )
}
}
抽取器里的unapply
方法,入参对应你想要进行匹配的对象,出参则是解构后的元素。
比如 list match { case Append(x,y) => }
里面的list
对应unapply的入参,x,y
对应unapply
方法的出参。
话说模式匹配(7) 一个构造器模式的例子(by case class)
在第一篇讲述构造器模式匹配的时候给出过tree的例子,因为tree的数据结构很适合用构造器模式来解构。这次再看另一个例子。
scala里的List是个典型的很适用模式匹配的结构,它的接口和数据定义非常凝练。现在我们假设需要一个与List结构正好相反的结构MyList。
List由2部分组成,[head, tail],其中的head是元素本身,而tail则是List类型,也就是一种递归结构。
MyList也由2部分组成 [init, last],其中last是元素本身,而init则是MyList类型。(与List正好颠倒)
// 定义抽象类
abstract class MyList[+A]
// 具体子类,数据由两部分组成:init,last
case class Cons[B] (init:MyList[B], last:B) extends MyList[B]
// 元素为空的MyList单例对象,类似 Nil
case object Empty extends MyList[Nothing]