【问题标题】:Why is synchronization not working properly within this code?为什么同步在此代码中无法正常工作?
【发布时间】:2017-02-03 02:04:27
【问题描述】:

在过去的几天里,我一直在阅读有关多线程的文章,并且遇到了一个使用多线程的简单任务。这是任务:

创建一个模拟 50 米跑步比赛的应用程序(在我的代码中,它们是 10 米,没关系)。跑步者的数量应该是 5 并且你应该命名每个跑步者线程。打印获胜者。所有其他线程也应该完成比赛。打印每个跑步者完成比赛所用的时间并突出显示获胜者的时间。

这是我写的代码:

public class Racer implements Runnable {
    public static String winner;
    public static int time = 0;

    public void incrementTime() {
        synchronized (Racer.class) {
            time++;
        }
    }
    public void race() {
        for (int distance = 1; distance <= 10; distance++) {
            incrementTime();
            System.out.println("Distance covered by " + Thread.currentThread().getName() + " is " + distance + " meters.");
            boolean finalDest = this.isTheRaceOver(distance);
            if (finalDest) {
                break;
            }
        }
    }
    private boolean isTheRaceOver(int finalDistance) {
        boolean isRaceOver = false;
        if (Racer.winner == null && finalDistance == 10) {
            String winnerName = Thread.currentThread().getName();
            Racer.winner = winnerName;
            System.out.println("The winner is : " + Racer.winner + " with time " + time);
            isRaceOver = true;
        } else if (Racer.winner == null) {
            isRaceOver = false;
        } else if (finalDistance != 10) {
            isRaceOver = false;
        } else if (finalDistance == 10) {
            System.out.println(Thread.currentThread().getName() + " is with time " + time);
        }
        return isRaceOver;
    }
    @Override
    public void run() {
        this.race();
    }
}

public class RacerDemo {
    public static void main(String[] args) {
        Racer racer = new Racer();
        Thread a = new Thread(racer, "A");
        Thread b = new Thread(racer, "B");
        Thread c = new Thread(racer, "C");
        Thread d = new Thread(racer, "D");
        Thread e = new Thread(racer, "E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}

一个输出是:

A 覆盖的距离是 1 米。
C所覆盖的距离为1米。
C覆盖的距离是2米。
C覆盖的距离是3米。
C覆盖的距离是4米。
C覆盖的距离是5米。
C覆盖的距离是6米。
C覆盖的距离是7米。
C覆盖的距离是8米。
C覆盖的距离是9米。
C覆盖的距离是10米。
获胜者是:C 时间为 12 // 应该是 11?
B所覆盖的距离为1米。
B 覆盖的距离为 2 米。
...... 等等

困扰我的是,当它打印每个赛车手(线程)跑完距离所用的时间时,它没有显示正确的时间。我使 incrementTime() 同步,但程序也不能正常工作。你能告诉我有什么问题吗?我的错在哪里?

【问题讨论】:

    标签: java multithreading synchronization


    【解决方案1】:

    每个跑步者都会增加时间,导致状态不一致。您应该将赛车手与实际比赛分开,这可能应该在一个单独的类中。

    您的问题出现在 Racer Runnable 的 race 方法中,其中每个 runner 递增 static time 字段,从而导致意外行为。

    【讨论】:

    • 这个解释是正确的。我不知道为什么它被否决了。
    • 是的,我知道问题来自 race() 但几分钟前我尝试在 for 循环中创建一个同步块,它似乎工作正常。我运行了几次。 . 但是谢谢!
    【解决方案2】:

    每个跑步者都会增加时间,并且在增加时间方法上只有一个锁。 Runner C 完成他们的比赛并调用 isRaceOver 方法。由于 c 将获得 11 的时间,线程 b 运行 race 方法,潜入并增加时间 1。结果,c 获得了 12 的时间并错误地打印出时间。如果您的意图是让所有跑步者都能够增加时间,则必须确保一次只有 1 个线程在进行比赛或 IsTheRaceOver。

    【讨论】:

      【解决方案3】:

      在您的实现中,time 由所有线程共享。因此,如果一个线程递增,则另一个线程将读取该值,例如:

      • 线程1进入race()时间值为0
      • 线程 1 调用 incrementTime() 将值更改为 1
      • 线程2进入race()时间值为1
      • 线程 2 调用 incrementTime() 将值更改为 2

      这将使time 变量处于不一致的状态。为避免这种情况,您可以尝试在 race() 方法中删除 time,以便每个线程都有自己的时间。

      【讨论】:

      • 感谢您的回答,但我还有其他想法。
      【解决方案4】:

      你会在没有同步块的情况下得到相同的结果,因为你同步了一个默认为原子的整数。不需要同步关键字,所以根本没有同步。

      A 覆盖的距离是 1 米。
      C所覆盖的距离为1米。
      C所覆盖的距离为2米。
      ...
      获胜者是:C 时间为 12 // 应该是 11 ?

      从您的输出中可以清楚地看出,另一个线程也增加了时间。在您打印获胜者之前,可能有另一个线程增加了时间。要解决您的问题,您必须同步 incrementTimeisTheRaceOver 方法,这样您可以确保在打印之前另一个线程无法增加时间。

      但请稍等: 同步块不保证更新对另一个线程可见,这称为缓存一致性。如果您在一个线程更新变量时不使用volatile 关键字,则不能保证另一个线程会看到它。例如,即使您设置了获胜者,另一个线程仍可能将其视为 null。

      确保使用 volatile 关键字或锁(也使用内存屏障)以保证更新对其他线程可见。

      【讨论】:

        猜你喜欢
        • 2016-03-27
        • 2017-09-05
        • 1970-01-01
        • 2020-11-17
        • 1970-01-01
        • 1970-01-01
        • 2019-06-28
        • 2017-02-06
        • 1970-01-01
        相关资源
        最近更新 更多