in Java

Java的线程同步

最近在啃 Learning Java , 对于资深微软系码工来说,理解上毫无故障 🙂 但要说 “C#就是抄的Java,会C# 就等于会了 Java”  这样的话就没意思了,中国厨子也使平底锅,煎牛排不见得会有人埋单。诚如书中所言:

We will see that the real appeal of Java is the combination of this simple language married with powerful tools and standards.

Learning Java

经过一周左右的热身,终于读到了Threads 章节,对于一个长期滞留在应用层面的码工来说,并发线程云云还是有几分吸引力的。

简单来说,线程是代码执行流的容器。对于没有刻意创建 Thread 对象的程序来说,亦用到了线程,这个线程即所谓的“主线程”。对于创建了多个 Thread 对象的程序来说,一段代码同时被多次调用执行(并发)是可能的,只是这些执行过程分别发生在各自的容器(Thread对象)。

我比较感兴趣的是多个执行流间的通信问题,即线程同步。

Java里的线程同步方案是基于 Monitor 的,本质上是 。锁被用于附加在资源上,在其被多线程同时访问时负责传递信号量,以保证访问是串行化的。

Java提供了一个synchronized 关键字以修饰方法或者代码块:在线程访问方法或代码块前,必须请求锁。注意,synchronized 可以修饰一个方法,也可以修饰一个代码块,如下所示:

在 Java VM 层面,Monitor 将被附加到synchronized 标注的方法或代码块,在线程请求同步时,进行协调(JVM的规范可参见官方的 Specification,对于滞留在应用层面的技术屌丝来说,如果你调试多线程程序遭遇到了java.lang.IllegalMonitorStateException 异常,多半不是JVM或者Monitor出了什么问题,而是你忘了加synchronized 关键字)。

配合synchronized 关键字,Java 在语言层面提供了几个方法:Object 类的wait 方法和notify 方法(notifyAll) 。通过这一对方法,我们可以显式的协调线程的执行流。可以这么理解它们的组合:

  • synchronized 代码块是上锁的,像卫生间一样,一次只能进一个人
  • wait 就是交出锁自己歇逼了,让别的线程有机会抢占卫生间
  • notify 就是通知已经歇逼了的线程,哥们要撤了,你们准备抢占卫生间(注意,并不是立即抢,是等当前线程执行完或者歇逼了)

还是举个粟子,原理说多了都是装逼,实干兴邦 🙂

假设有这么个程序:有两个Thread 小白和小黑(Thread white & Thread black)在抢占一个Resource ,小白先抢到说句话然后歇逼,小黑随后进来说句话然后通知小白准备进来自己歇逼,小白执行完任务后通知小黑起来,小黑执行完成 Orz。它的时序图表示如下:

Synchronized Threads Sequence Diagram

再看代码,Resource 类代码:

Main 入口代码:

用Eclipse 运行一下,Console 输出和期望一致:

Synchronized Threads Executive Result

说句题外话:重型语言(Java/C#…)社区有一点好,IDE (Eclipse/Visual Studio…)提供了完备的Debug 工具,使得使用者得以洞悉执行流,形成线程串行化的抽象思维,所以,我并不认同 “Debug 低级,Log 高级”的说法。

打赏作者
您的支持将激励我继续创作!

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

Write a Comment

Comment

  1. 有感于最后一句,CUDA的memcheck工具刚刚添加了显示内存访问错误行号的功能(之前只能显示出错的代码文件,哪一行出错了得自己找…),我们CUDA程序员还处于茹毛饮血的调试时代…

    • 调试没有高低之分,关键是清楚自己的程序是怎么运行的,并且对执行流可能的分支有所把握,用Log也并无不好,只要能dump到关键信息。
      TDD里的代码覆盖率设计是有道理的:)