java - 使用等待和信号方法进行同步

标签 java multithreading semaphore

使用手动等待和信号方法的信号量的java实现似乎不起作用。可能出了什么问题?

class Runner extends Thread implements Runnable{

    public static int s=1;
    private static int c;
    private String tname;

    Runner(){
        tname=this.getName();
    }
    public void wait(int s){
        while(s==0)
            System.out.println(tname+" Waiting; s = "+s);
        s--;
        System.out.println(tname+" Wait over; s = "+s);
    }

    public void signal(int s){
        s++;
        System.out.println(tname+" Signalled; s ="+s);
    }

    public void run(){
        wait(s);
        //critical section begin
        go();
        //critical section end
        signal(s);
    }

    public void go(){
        int f=10;
        while(f-->0){
            c++;
            System.out.println(tname+" : Counter = "+c);
        }
    }

}

public class wns{
    public static void main(String[] args){
        Runner t1=new Runner();
        Runner t2=new Runner();
        t1.start();
        t2.start();
    }
}

我在 Ubuntu 14.04 LTS 上运行它并得到了意外的输出。 输出

Thread-1 Wait over; s = 0
Thread-0 Wait over; s = 0
Thread-0 : Counter = 2
Thread-0 : Counter = 3
Thread-0 : Counter = 4
Thread-0 : Counter = 5
Thread-0 : Counter = 6
Thread-0 : Counter = 7
Thread-0 : Counter = 8
Thread-0 : Counter = 9
Thread-0 : Counter = 10
Thread-0 : Counter = 11
Thread-0 Signalled; s =2
Thread-1 : Counter = 1
Thread-1 : Counter = 12
Thread-1 : Counter = 13
Thread-1 : Counter = 14
Thread-1 : Counter = 15
Thread-1 : Counter = 16
Thread-1 : Counter = 17
Thread-1 : Counter = 18
Thread-1 : Counter = 19
Thread-1 : Counter = 20
Thread-1 Signalled; s =2

即使 s 增加一次,其值也将变为 2。同样,当递减时,它再次变为 0,对同步完全没有影响。有人可以解释一下这里到底发生了什么吗?

最佳答案

两个线程都没有在等待。

两个线程大致同时进入wait()方法。两个线程大致同时检查是否 s == 0。两个线程几乎同时判定 s 确实等于 0。然后,两个线程大致同时递减 s。减量不是原子操作,并且两个线程同时尝试减量,因此可能只有一个减量实际上有效。 (因此在两者都调用减量后 s 为 0)。然后两个线程同时调用 signal。两个线程大致同时调用增量运算符。这次两个增量都发生了,因此 s 现在是 2。

真正的信号量和互斥体通常需要特殊的处理器调用来执行原子测试和设置操作。 Java 不允许您访问该操作(尽管它的同步机制肯定在幕后使用它),因此,如果不使用某种 Java 锁定机制(例如同步),您就无法编写自己的信号量类。

编辑:我在一件事上错了。 Java 确实允许使用位于 java.util.concurrent.atomic 包中的原子原语包装器(例如 AtomicInteger)访问compareAndSet 方法。您可以使用它们来创建自己的信号量,而无需使用synchronized 关键字。

示例:为了让大家明白这一点,如果 s 是 AtomicInteger,您可以将 while 循环替换为:

boolean waiting = true;
while(waiting) {
  int stableSValue = s.get();
  if(stableSValue == 0) {
    System.out.println("Waiting.  S was 0");
  } else {
    if(s.compareAndSet(stableSValue, stableSValue-1)) {
      System.out.println("Wait done.");
      waiting = false;
    } else {
      System.out.println("Optimistic locking failure.  Trying again.");
    }
  }
}

关于java - 使用等待和信号方法进行同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26341261/

相关文章:

继承中的java异常处理

java - 为什么要使用 java 容器?

java - 如何使 ImageIO 从 InputStream :Java 读取

multithreading - 如何制作原子指令

c++ - Unix 信号量 - 无法设置初始值

java - 如何将刻度从 java 转换为带有时区的 DateTime c#

c - pthread 示例中的查询 regaring 代码序列

web-services - 从线程调用远程 SOAP 调用时发生异常

c++ - C++ 中用于可变长度记录的无锁共享内存

Java:如果有的话,除了它们所属的对象之外,什么被同步方法锁定了?