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

Java 使用等待和信号方法进行同步,java,multithreading,semaphore,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){

使用手动等待和信号方法的信号量的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();
    }
}
我在Ubuntu14.04LTS上运行了它,得到了一个意外的输出。 输出


s的值变为2,即使它增加一次。同样,当递减时,它再次变为0,对同步没有任何影响。有人能解释一下这里到底发生了什么吗?

两个线程都没有等待

两个线程大致同时进入
wait()
方法。两个线程大致同时检查
s==0
。两个线程决定,是的,s大致同时等于0。然后,两个线程大致同时减小s。减量不是一个原子操作,如果两个线程同时尝试减量,则可能只有一个减量真正起作用。(因此,在两者都调用减量后,s为0)。然后两个线程同时调用信号。两个线程几乎同时调用增量运算符。这一次,两个增量恰好都起作用,因此s现在是2

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

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

示例:为了使点返回原点,如果s是一个原子整数,则可以将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.");
    }
  }
}

两个线程都没有等待

两个线程大致同时进入
wait()
方法。两个线程大致同时检查
s==0
。两个线程决定,是的,s大致同时等于0。然后,两个线程大致同时减小s。减量不是一个原子操作,如果两个线程同时尝试减量,则可能只有一个减量真正起作用。(因此,在两者都调用减量后,s为0)。然后两个线程同时调用信号。两个线程几乎同时调用增量运算符。这一次,两个增量恰好都起作用,因此s现在是2

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

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

示例:为了使点返回原点,如果s是一个原子整数,则可以将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.");
    }
  }
}

两个线程都没有等待

两个线程大致同时进入
wait()
方法。两个线程大致同时检查
s==0
。两个线程决定,是的,s大致同时等于0。然后,两个线程大致同时减小s。减量不是一个原子操作,如果两个线程同时尝试减量,则可能只有一个减量真正起作用。(因此,在两者都调用减量后,s为0)。然后两个线程同时调用信号。两个线程几乎同时调用增量运算符。这一次,两个增量恰好都起作用,因此s现在是2

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

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

示例:为了使点返回原点,如果s是一个原子整数,则可以将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.");
    }
  }
}

两个线程都没有等待

两个线程大致同时进入
wait()
方法。两个线程大致同时检查
s==0
。两个线程决定,是的,s大致同时等于0。然后,两个线程大致同时减小s。减量不是一个原子操作,如果两个线程同时尝试减量,则可能只有一个减量真正起作用。(因此,在两者都调用减量后,s为0)。然后两个线程同时调用信号。两个线程几乎同时调用增量运算符。这一次,两个增量恰好都起作用,因此s现在是2

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