引言

  最近工作当中写了一个有关并发的程序,引起了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%利用率的情况。

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

小结

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

  

我们一起来聊聊并发吧,one。的更多相关文章

  1. 聊聊并发(七)——Java中的阻塞队列

    3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...

  2. 聊聊并发-Java中的Copy-On-Write容器

    详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp78   聊聊并发-Java中的Copy-On-Write容器   Cop ...

  3. 聊聊并发(一)深入分析Volatile的实现原理

    本文属于作者原创,原文发表于InfoQ:http://www.infoq.com/cn/articles/ftf-java-volatile 引言 在多线程并发编程中synchronized和Vola ...

  4. 转:聊聊并发(八)——Fork/Join框架介绍

    1. 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架. 我们再通过 ...

  5. 聊聊并发(六)——ConcurrentLinkedQueue的实现原理分析

    1. 引言 在并发编程中我们有时候需要使用线程安全的队列.如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法.使用阻塞算法的队列可以用一个锁(入队和出队用同一把 ...

  6. 聊聊并发(三)Java线程池的分析和使用

    1.    引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行. ...

  7. 聊聊并发——深入分析ConcurrentHashMap

    术语定义 术语 英文 解释 哈希算法 hash algorithm 是一种将任意内容的输入转换成相同长度输出的加密方式,其输出被称为哈希值. 哈希表 hash table 根据设定的哈希函数H(key ...

  8. Java并发编程原理与实战一:聊聊并发

    一.大纲 •你真的了解并发吗 •多线程和并发 •多线程和多进程 •线程一定快吗 •学习并发的四个阶段 •学习目标 •适合人群 •荐书   二.学习并发的四个阶段 •熟练掌握API,能够完成并发编程 • ...

  9. 聊聊并发(四)——深入分析ConcurrentHashMap

    线程不安全的HashMap 因为多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap,如以下代码   final HashM ...

随机推荐

  1. windows命令行大全

    命令简介 cmd是command的缩写.即命令行 . 虽然随着计算机产业的发展,Windows 操作系统的应用越来越广泛,DOS 面临着被淘汰的命运,但是因为它运行安全.稳定,有的用户还在使用,所以一 ...

  2. s = sorted(lst,key= func) # 将列表中的元素一个一个传给func,根据func的返回值进行排序

    排序函数sorted :语法: sorted(iterable,key=func , reverse )key:排序规则(排序函数),在sorted内部将"可迭代对象"中的每一个元 ...

  3. VRSProcess(一)

    1.freopen( "CONOUT$","w",stdout);在操作系统中,命令行控制台(即键盘或者显示器)被视为一个文件,既然是文件,那么就有“文件名”. ...

  4. 树&二叉树&&满二叉树&&完全二叉树&&完满二叉树

    目录 树 二叉树 完美二叉树(又名满二叉树)(Perfect Binary Tree) 完全二叉树(Complete Binary Tree) 完满二叉树(Full Binary Tree) 树 名称 ...

  5. [DAViCHi/SeeYa/T-ARA][원더우먼][Wonder Woman]

    歌词来源:http://music.163.com/#/song?id=5371229 作曲 : 赵英秀 [作曲 : 赵英秀] [作曲 : 赵英秀] 作词 : K-Smith [作词 : KSmith ...

  6. 使用MS的ScriptDom来拆解TSQL脚本

    此处提供9.1.40413.0版本的DLL一共4个:Microsoft.Data.Schema.dll.Microsoft.Data.Schema.ScriptDom.dll.Microsoft.Da ...

  7. Java虚拟机17:互斥同步、锁优化及synchronized和volatile

    互斥同步 互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保证手段.同步是指子啊多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一 ...

  8. 1834. [ZJOI2010]网络扩容【费用流】

    Description 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用. 求:  1.在不扩容的情况下,1到N的最大流:  2.将1到N的最大流增加K所需 ...

  9. centos6.5添加阿里docker加速器

    1. 配置阿里docker加速器 vi /etc/sysconfig/docker 在文件末尾追加下面两行 other_args="--registry-mirror=https://pl8 ...

  10. java的@PostConstruct注解

    javax.annotation 注释类型 PostConstruct @Documented @Retention(value=RUNTIME) @Target(value=METHOD) publ ...