什么 是 多线程呢 ?html
举个 例子:java
若是要计算很大的 Fibonacci 数,是不能使用该算法的,由于其中有大量的重复计算。图 27.1 展现了在计算 F6 时所建立的递归过程实例树。其中,对于对于 FIB(6) 的调用会递归地调用 FIB(5) 和 FIB(4) 。而对 FIB(5)的调用又会去调用 FIB(4) 。这两个 FIB(4) 实例返回的结果彻底相同( F4 =3 )。因为 FIB 并无去记住这些结果,所以对于 FIB(4) 的第二次调用重复了第一次调用的工做。算法
把多线程计算(由一个表明多线程程序的处理器执行的运行时指令集)看作是一个有向无环图 G= ( V, E )是颇有帮助的,咱们称其为计算 dag ( directed acyclic graph ) 。图 27.2多线程
中给出了一个示例,其中的计算 dag 来自计算 P-FIB(4) 。从概念层面来说, V 中的顶点都是指令, E 中边则表示指令间的依赖关系, (u,v ) ∈ E 表示指令 u 必须在指令 v 以前执行。为了方便起见,若是一条指令链中不包含任何并行控制语句(没有 spawn 、 sync 以及被 spawn 例程中 return ——显式的 return 语句或者过程执行完后的隐式 return ),咱们就把它们组成一组,造成一个 strand ,每一个 strand 都表示一条或者多条指令。涉及并行控制的指令不包括在 strand 中,可是会出如今 dag 结构中。例如,若是一个 strand 有两个后继,那么其中之一必须得被spawn 出来,若是一个 strand 有多个前驱,就表示前驱由于一条 sync 语句被合并在一块儿。所以,通常来讲, V 造成了 strand 集合,而有向边集合 E 则表示由并行控制产生的 strand 间的依赖关系。若是 G 中有一个从 strand u 到strand v 的有向路径,那么咱们就说这两个 strands 是(逻辑上)串行的。不然就称其为(逻辑上)并行的。并发
先来两张图:工具
各类状态一目了然,值得一提的是"blocked"这个状态:
线程在Running的过程当中可能会遇到阻塞(Blocked)状况post
此外,在runnable状态的线程是处于被调度的线程,此时的调度顺序是不必定的。Thread类中的yield方法可让一个running状态的线程转入runnable。性能
synchronized, wait, notify 是任何对象都具备的同步工具。让咱们先来了解他们测试
他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确monitor的概念,Java中的每一个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥做用,反之若是在synchronized 范围内,监视器发挥做用。优化
wait/notify必须存在于synchronized块中。而且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait以后,其余线程能够进入同步块执行。
当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去wait或notify,会抛出java.lang.IllegalMonitorStateException。也包括在synchronized块中去调用另外一个对象的wait/notify,由于不一样对象的监视器不一样,一样会抛出此异常。
咱们以一个真实的故事来结束本小节,该故事发生在世界级多线程国际象棋对弈程序 Socrates[81] 的开发期间(时间作了简略)。该程序的原型是在一个具备 32 个处理器的计算机上进行的,可是最终运行在一个具备 512 个处理器的超级计算机上。某天,开发人员对程序进行了一项优化,针对一项重要的在 32 个处理器上基准测试( benchmark ),把其运行时间从 T32 = 65 秒减小到 T’32 =40 秒。然而,开发人员使用关于 work和 span 的性能度量得出结论:在 32 个处理器上更快的优化版本,在 512 个处理器上运行时,实际上比原始版本慢一些。结果,他们放弃了“优化”。
他们的分析方法是这样的。程序的原始版本的 work T1 = 2048 秒, span T∞ = 1 秒。若是把不等式 (27.4)看作是等式, TP = T1 /P+T∞ ,并把它看成在 P 个处理器上的近似运行时间,确实能够得出, T32=2048/32+1 = 65 。若是作了优化, work 变成 T’1 =1024 秒, span 变成 T’∞ = 8 秒。采用一样的近似方法,有 T’32 =1024/32 + 8=40 。
可是,当在 512 个处理器上进行计算运行时间时,这两个版本的相对速度会发生转换。此时, T512=2048/512+1 = 5 秒, T’512 =1024/512+8 = 10 秒。在 32 个处理器上可以提速程序的优化版本在 512 个处理器上会使得程序慢 2 倍!优化版本的 span 为 8 ,对于 32 个处理器来讲,其不是运行时间中的支配项,可是在 512 个处理器时,就变成了支配项,把更多核的优点化为乌有。
这个故事的寓意在于, work 和 span 是一种好的推断性能的方法,而不是一种好的推断运行时间的方法。