在Java中,锁定是否确保读取和写入非易失性位置的正确顺序

时间:2014-02-27 13:01:42

标签: java multithreading

假设我有一个类的共享对象:

class Shared {
    private int x = 0 ;
    private int y = 0 ;
    private Semaphore s = new Semaphore() ;

    public int readX() {
        s.p() ;
        int x0 = x ;
        s.v() ;
        return x0 ; }

    public void writeX(int x0) {
        s.p() ;
        x = x0 ;
        s.v() ; }

    public int readY() {
        s.p() ;
        int y0 = y ;
        s.v() ;
        return y0 ; }

    public void writeY(int y0) {
        s.p() ;
        y = y0 ;
        s.v() ; } }

此处信号量是一个使用 synchronized 方法提供互斥的类。

现在发生以下行动:

  • 线程0调用o.readX()并发现它为0。
  • 线程1使用o.writeX(1)将1写入x然后将1写入y; o.writeY(1);
  • 线程0调用o.readY()得1,然后调用o.readX()。

线程0是否可以从其缓存读取并发现x为0?为什么呢?


修改

这是Semaphore类

class Semaphore {
    private boolean available = true ;

    public synchronized void v() {
        available = true ; notifyAll() ; }

    public synchronized void p() {
        while( !available ) try{ wait() ; } catch(InterruptedException e){} 
        available = false ; }
}

2 个答案:

答案 0 :(得分:3)

根据JMM,以下序列严格按先发条件关系排序,因此保证在步骤4中步骤1中对读者所做的更改的可见性:

  1. 写给x
  2. 退出监视器M保护的同步块
  3. 进入由监视器M保护的同步块
  4. 从x
  5. 读取

    写入变量发生 - 在释放监视器之前,释放监视器 - 在获取同一监视器并获取监视器之前 - 在从变量读取之前发生。

    因此,如果同步pv,则线程0将看到对x的更改。

答案 1 :(得分:3)

并排显示两个主题的动作:

Thread 1                       Thread 2
o.readX(); // 0                o.writeX(1);
o.readY(); // 1                o.writeY(1);
o.readX(); // must be 1

Semaphore方法的任意选择的调用对进行严格的发生 - 排序,因为它们都在同一个共享{{1}上进行获取 - 释放循环实例。

我们有:

    线程2的
  • Semaphore 发生在之前 o.writeX(1);
  • o.writeY(1)由线程2 在线程1之前发生 o.writeY(1);
  • 线程1 发生在第二个o.readY() -> 1之前由线程1发生
  • o.readY();
  • 因此线程2 发生在{/ 1}之前线程1的第二个o.readX();
  • 因此第二个o.writeX(1)的结果必须为1.