繁体   English   中英

使用同步顺序执行线程

[英]Sequential execution of threads using synchronized

我有一小段代码创建了3个线程,并期望它们使用整数对象上的同步块顺序打印。 但是显然我有时会陷入僵局。 见下文:

public class SequentialExecution implements Runnable {

    private Integer i = 1;

    public void run() {

        String tmp = Thread.currentThread().getName();

        if (tmp.equals("first")) {
            synchronized(i) {
                first();
                i = 2;
            }
        } else if (tmp.equals("second")) {
            while (i != 2);
            synchronized(i) {
                second();
                i = 3;
            }
        } else {
            while (i != 3);
            synchronized(i) {
                third();
            }
        }

    }

    public void first() {
        System.out.println("first " + i);
    }

    public void second() {
        System.out.println("second " + i);
    }

    public void third() {
        System.out.println("third " + i);
    }

    public static void main(String[] args) {
        //create 3 threads and call first(), second() and third() sequentially
        SequentialExecution se = new SequentialExecution();
        Thread t1 = new Thread(se, "first");
        Thread t2 = new Thread(se, "second");
        Thread t3 = new Thread(se, "third");

        t3.start();
        t2.start();
        t1.start();

    }

}

我期望(有时会得到)的结果是:

first 1
second 2
third 3

我遇到死锁(并且日食挂起)的一个示例结果是:

first 1
second 2 

有人知道为什么这行不通吗? 我知道我可以使用锁,但是我不知道为什么使用同步块不起作用。

声明ivolatileprivate volatile Integer i = 1; 这会警告编译器不要对i应用某些优化。 每次引用它时,都必须从内存中读取它,以防其他线程对其进行了更改。

我还与user3582926的答案建议同意就同步this ,而不是i ,因为对象通过引用i的变化作为程序运行。 使程序正常运行既不必要也不充分,但是确实使它变得更好,更清晰。

我已经通过将主要方法更改为测试了每个更改:

  public static void main(String[] args) throws InterruptedException {
    // create 3 threads and call first(), second() and third() sequentially
    for (int i = 0; i < 1000; i++) {
      SequentialExecution se = new SequentialExecution();
      Thread t1 = new Thread(se, "first");
      Thread t2 = new Thread(se, "second");
      Thread t3 = new Thread(se, "third");

      t3.start();
      t2.start();
      t1.start();
      t1.join();
      t2.join();
      t3.join();
    }

  }

没有死锁。 存在内存顺序问题。

第二个和第三个线程中的while循环在任何同步块之外。 没有任何内容告诉编译器和JVM在循环期间这些线程无法将i或其指向的对象保留在寄存器或高速缓存中。 结果是,根据时序,这些线程中的一个可能会陷入循环,从而观察到不会改变的值。

解决该问题的一种方法是将i标记为volatile。 这会警告编译器它正在用于线程间通信,并且每当i更改时,每个线程都需要监视内存内容的更改。

为了完全使用同步解决问题,您需要检查在单个特定对象上同步的块内i引用的Integer的值。 i对此没有好处,因为它会由于装箱/拆箱转换而发生变化。 也可能是一个简单的int

同步块无法包装while循环,因为这实际上会导致死锁。 相反,同步块必须在循环内部。 如果对i的更新是在同一对象上同步的,则这将迫使更新对while循环内的测试可见。

这些考虑因素导致产生以下基于同步的版本。 我正在使用执行1000次运行的main方法,并且如果其中任何一个运行中的任何线程挂起,它本身都会挂起。

public class SequentialExecution implements Runnable {

  private int i = 1;

  public void run() {

    String tmp = Thread.currentThread().getName();

    if (tmp.equals("first")) {
      synchronized (this) {
        first();
        i = 2;
      }
    } else if (tmp.equals("second")) {
      while (true) {
        synchronized (this) {
          if (i == 2) {
            break;
          }
        }
      }
      synchronized (this) {
        second();
        i = 3;
      }
    } else {
      while (true) {
        synchronized (this) {
          if (i == 3) {
            break;
          }
        }
      }

      synchronized (this) {
        third();
      }
    }

  }

  public void first() {
    System.out.println("first " + i);
  }

  public void second() {
    System.out.println("second " + i);
  }

  public void third() {
    System.out.println("third " + i);
  }

  public static void main(String[] args) throws InterruptedException {
    // create 3 threads and call first(), second() and third() sequentially
    for (int i = 0; i < 1000; i++) {
      SequentialExecution se = new SequentialExecution();
      Thread t1 = new Thread(se, "first");
      Thread t2 = new Thread(se, "second");
      Thread t3 = new Thread(se, "third");

      t3.start();
      t2.start();
      t1.start();
      t1.join();
      t2.join();
      t3.join();
    }

  }
}

我相信您想使用synchronized(this)而不是synchronized(i)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM