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

时间:2014-10-13 13:32:45

标签: 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,对同步完全没有影响。有人能解释一下这里到底发生了什么吗?

1 个答案:

答案 0 :(得分:2)

两个线程都没有等待。

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

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

编辑:我错了一件事。 Java允许使用位于AtomicInteger包中的原子原始包装器(例如java.util.concurrent.atomic)访问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.");
    }
  }
}