我们一起来聊聊并发吧,one。

发布时间:2013-11-24 22:08:00作者:左潇龙阅读(2730 )评论(21)

    引言

     

      最近工作当中写了一个有关并发的程序,引起了LZ对并发的强烈兴趣。这一下一发不可收拾,LZ用了一个多星期,看完了这本共280+页的并发编程书。之所以能看这么快,其实这主要归功于,自己之前对并发就有一定的理解。在这种前提下看书,其实只是一个印证自己之前想法的过程而已,因此看起来会比较快,而且在看的时候,会有多次这种感觉,“擦,原来还真是这样的”。

      尽管LZ已经说了看书看的快的原因,但不管怎么说,书看的太快,肯定难免有遗漏。因此博客此时就派上用场了,它绝对可以帮你查缺补漏。因为在写的过程中,你会发现,之前你读的时候自以为理解的透透的东西,却无法给别人讲清楚。这就说明,你需要补补漏了。

     

    并发的来源

     

      并发的由来,从现在来看,似乎是必然的。因为人是一种懒惰而又急躁的动物,这是人的本性。因为急躁,并发就出现了,因为懒惰,多核时代就来了。

      为什么这么说呢?

      如果所有的程序都是串行的(之所以说如果,是因为LZ接触到电脑时,它已经是并发的了,因此只能想象一下),那么当你打开了一个特别慢的网页,最后你等不及想关掉浏览器的时候,你会发现,你必须等加载这个网页的事做完你才能关闭它。这是何其蛋疼的一件事。急躁的人们能允许这种事发生?因此并发就出现了。

      再来想象一下,如果我们想让一个程序运行的更快,从直觉上讲,我们应该让CPU运算的更快。比如以前一秒可以计算1000次,现在我们让它在一秒内可以计算10000次。但是懒惰的人们发现,这种直觉上的方式似乎非常困难,想要硬生生的提高CPU的速度(缩短时钟周期)是非常困难的。因此懒惰的人们就想到一种偷懒的办法,一个CPU一秒可以计算1000次,两个的话不就可以计算2000次了(实际并非如此,但我们可以这么理解多个CPU带来的效率增加),这个相对简单的办法最终被人们采用了。因此多核时代就到来了。

      

    并发的危险

      

      说起并发引起的危险,在LZ的理解来看,主要来源于程序所给人带来的直觉造成的误导。这一点在LZ所写的计算机系统原理中有讲到(可以将这两本书的内容联系起来),比如下面这个程序,它给人的直觉是,a首先变成了1,然后b变成了2。

        int a = 1;
        int b = 2;

      直觉是这样的,但往往是错误的,因为在程序真正执行的时候,可能是b先变成了2,a又变成了1,更奇葩的是,很可能a和b都始终是0。估计说起两者的赋值顺序颠倒,各位还可以理解,但是说到两者可能都是0,有的猿友就懵了,有种瞬间被颠覆三观的感觉,但是学习并发往往就是颠覆你三观的过程。

      这种直觉与现实之间的不同,就给并发的程序造成了危险。它可能引起你预料之外的错误,而且往往是防不胜防。因此并发是诱人的,但也同样是危险的,一个诱人的东西永远都伴随着危险,就像高贵的玫瑰往往都是有刺的。

      知道了上面这些,我们就可以来看看安全性和活跃性了。安全性是指,“程序不会出现糟糕的事情”,活跃性则是指,“好的事情一定会发生”。可以看出来,安全性更多的是在强调执行的程序是正确的,而活跃性更多的是在强调程序可以正确的往下进行(有点绕?那就对了)。

      举个例子,对于一个并发递归求解的程序来讲,安全性则可以保证结果的正确性,而活跃性则可以保证这个程序总能得到一个正确的结果或者最终发现它没有解而抛出无解的异常。

      

    Java的并发

     

      对于大部分从事Java开发不久的程序猿来讲(包括LZ),并发一般都是很少接触到的(这里主要以LZ的领域来说,即J2EE),因为现有的框架已经将很多并发的问题给解决了,并给我们这些无脑程序猿创造了一个串行程序的环境。

      比如,在J2EE领域的servlet规范当中,servlet是单例的,并且非常有可能,甚至可以说一定会被多个线程并发的去访问。因此servlet其实是有并发的安全性问题的,除非你不在servlet当中记录任何状态。但是当前比较火的MVC框架struts2已经帮我们解决了这个问题,尽管Action当中经常会有一些数据或者说状态,但Action在struts2中是非单例的,这相当于每个Action实例都是线程私有的,因此不存在并发问题。

      有一些情况下,我们可能会接触到并发问题,比如,你需要做一个单例的对象,那么这个对象一般可以被全局访问,因此就可能存在并发的问题。可以这么说,几乎所有采用了单例模式的对象都会涉及到并发的问题,除非这个对象没有任何状态,但是这往往不会出现,因为没有状态的单例对象是没有意义的,它们更好的处理方式应该是一个无实例(即将构造函数私有化)且充满了静态方法的类。

      很多程序猿在初次意识到并发时,都会采取一个看似万能却并非一定有用的方法,那就是将一个类的所有方法加上synchronized关键字。LZ以前也是这样的,而且还自认为十分高端,现在想想,LZ实在自惭形秽。当时LZ对synchronized的理解,就知道它可以让很多线程一个一个来执行这个方法,至于其它的特性,就不太明白了。

      其实在大部分时候,一些比较简单的场景中,上面这种无脑做法还是能起到相应的作用的,也就是说,它可以保证安全性与活跃性。但是另外一个特性就无法保证了,那就是性能。无脑的方法同步,有时候会将性能降低数个数量级,可能会使很多线程都在等待,CPU却一直处于1%利用率的情况。

      通常情况下,对于安全性、活跃性以及性能来说,我们会将性能放在最后一位,引用之前看过的一句经典的话,是用来形容面向对象设计的,即“可复用的前提是可用”。同样的,对于并发的程序来讲,“性能高的前提是程序的执行是正确的”。

     

    小结

     

      今天就暂且写这么多吧,对于并发,其实想说的还有很多,毕竟刚看完这本书。后面还会陆续给出自己的理解,但是会穿插着《计算机系统原理系列》的内容,这个系列也该继续前进了,因为并发已经耽误了它太久。

      


    版权声明:本文版权归作者(左潇龙)所有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    6
    精彩
    0
    感动
    0
    搞笑
    0
    开心
    0
    愤怒
    0
    无聊
    0
    灌水
    0
    惊讶
#1楼     时间:2013-11-24 22:54:00      来源:风言风语^-^
不知道楼主对MPI了解多少,很想咨询一下关于进程间通信的一些问题。
#2楼     时间:2013-11-24 23:31:00      来源:D10.天地弦
楼主转行当作者吧
#3楼     时间:2013-11-24 23:31:00      来源:D10.天地弦
比程序猿好
#4楼     时间:2013-11-25 00:04:00      来源:左潇龙
@ Karlvin
贫僧愚钝,MPI不太了解,百度了一下好像是个通信协议,0.0。Java一般都是单个进程,所以很少涉及进程间的通信,线程的相对来说会比较多点,让施主失望了,抱歉。
#5楼     时间:2013-11-25 00:05:00      来源:左潇龙
@ D10.天地弦
作者,其实也是苦命人。。。。贫僧还是先淡定着吧。。。。
#6楼     时间:2013-11-25 11:26:00      来源:风言风语^-^
@ 左潇龙
楼主谦虚了。码农不必作践码农。 MPI的书籍看了一些,可是真正实践的时候,我连个环境都搭不对。。。
#7楼     时间:2013-11-25 16:05:00      来源:hb_icer
好好写吧...会跟进的, 写吧,写上几篇回头转到我的博客和网站上去....
#8楼     时间:2013-11-25 18:07:00      来源:夜の魔王
@ Karlvin
.net里面的WCF就是进程间通信。
#9楼     时间:2013-11-25 19:39:00      来源:天书说
@ D10.天地弦
引用楼主转行当作者吧

非常同意!
#10楼     时间:2013-11-25 21:24:00      来源:风言风语^-^
@ 夜の魔王
有没有那种平台无关的版本?我都想直接用Socket来通信了。但是这个方式过于简单粗暴,我怕操作系统这贱内受不住啊。
#11楼     时间:2013-11-26 09:29:00      来源:夜の魔王
#12楼     时间:2013-11-26 09:40:00      来源:风言风语^-^
@ 夜の魔王
你觉得,用Socket这个方式的并行还有意义吗?我觉着有点不靠谱。
如果联机,socket一直活着,感觉会占掉很多资源;否则的话,每次重启,不科学。
#13楼     时间:2013-11-26 13:27:00      来源:夜の魔王
@ Karlvin
你说要用Socket,我没说。如果你只是轻量级的通信,用完就释放,还要考虑并发,我觉得用Node.JS做是不错的选择。
#14楼     时间:2013-11-26 13:49:00      来源:风言风语^-^
@ 夜の魔王
又是一个高端的东西,臣妾不会啊。Not even heard of it. 囧
#15楼     时间:2013-11-26 21:07:00      来源:Raphael_java
曾一直以为,每一次访问,就是一条线程,每条线程里的对象都是互不干扰的。唉
#16楼     时间:2013-12-22 15:41:00      来源:永志
楼主,您好,您所说的这本共280+页的并发编程书叫什么名字啊,楼主推荐一下,谢谢哈
#17楼     时间:2014-11-12 17:40:00      来源:jimcsharp
“看完了这本共280+页的并发编程书” :
请问楼主是哪本书?
#18楼     时间:2014-12-26 17:04:00      来源:_辉
楼主你看了书,不把书名发出来,不厚道!
#19楼     时间:2015-06-01 18:34:32      来源:福州市网友
#20楼     时间:2015-06-10 23:38:00      来源:little_mq
《JAVA 并发编程实战》
#21楼     时间:2017-04-21 10:45:00      来源:鲍宏飞
学习往往就是颠覆三观的过程。
发表评论

站内搜索