为什么在这段代码中同步没有正常工作?

4
在过去的几天里,我一直在阅读有关多线程的文章,并遇到了一个使用多线程的简单任务。这是任务:
创建一个应用程序,模拟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()同步化,但程序仍然无法正常工作。你能告诉我错在哪里吗?我的错误在哪里?

4个回答

2
每个跑步者都在增加时间,导致状态不一致。您应该将赛车手与实际比赛分开,这可能需要使用一个单独的类。
您的问题出现在Racer Runnable的race方法中,每个跑步者都在增加静态时间字段,从而导致意外行为。

2
这个解释是正确的。我不知道为什么它被踩了。 - Darshan Mehta
是的,我知道问题出在race()函数上,但几分钟前,我尝试在for循环内部创建了一个同步块,似乎可以正常工作...我已经多次运行它了...但还是谢谢你! - Roxy

1
在您的实现中,time被所有线程共享。因此,如果一个线程增加它,另一个线程将读取该值,例如:
  • 线程1进入race(),时间值为0
  • 线程1调用incrementTime()将值更改为1
  • 线程2进入race(),时间值为1
  • 线程2调用incrementTime()将值更改为2
这将使time变量处于不一致的状态。为避免这种情况,您可以尝试在race()方法内声明time,以便每个线程都有自己的时间。

谢谢你的回答,但我想到了其他的事情。 - Roxy

0

由于您同步了一个默认情况下为原子的整数,因此您将得到与未同步块相同的结果。同步关键字是不必要的,因此根本没有同步。

A 跑了 1 米。
C 跑了 1 米。
C 跑了 2 米。
...
冠军是:C,用时12 // 应该是11吗?

从输出结果中可以看出,另一个线程也增加了时间。可能有另一个线程在打印赢家之前就已经增加了时间。要解决这个问题,您必须同时同步 incrementTimeisTheRaceOver 方法,这样您就可以确保另一个线程无法在您打印之前增加时间。

但是:同步块不能保证更新对其他线程可见,这称为缓存一致性。如果您在更新变量时不使用 volatile 关键字,则不能保证另一个线程将看到它。例如,即使您设置了赢家,另一个线程仍然可能将其视为空。

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


0
每个跑步者增加时间,只有增加时间方法上有锁。 跑步者C完成比赛并调用isRaceOver方法。 当c要获得11的时间时,线程b运行race方法,悄悄地增加1的时间。 结果,c获得12的时间并错误地输出时间。 如果您的意图是让所有跑步者都能增加时间,您必须确保一次只有1个线程在运行race或IsTheRaceOver。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接