Java同步问题

knp*_*wrs 2 java concurrency thread-safety

我刚刚开始学习Java并发性,我从以下代码得到一些奇怪的输出:

ThreadSafe.java:

public class ThreadSafe {
    private int value;

    public ThreadSafe() {
        value = 0;
    }

    public synchronized int getNext() {
        if (value >= 10000) {
            value = 0;
        }
        return value++;
    }
}
Run Code Online (Sandbox Code Playgroud)

RunnableImpl.java:

public class RunnableImpl implements Runnable {
    private String name;
    private ThreadSafe ts;

    public RunnableImpl(String name, ThreadSafe ts) {
        this.name = name;
        this.ts = ts;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(name + ": " + ts.getNext());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Main.java:

public class Main {
    public static void main(String[] args) {
        ThreadSafe ts = new ThreadSafe();

        Runnable a = new RunnableImpl("A", ts);
        Runnable b = new RunnableImpl("B", ts);

        new Thread(a).start();
        new Thread(b).start();
    }
}
Run Code Online (Sandbox Code Playgroud)

每当线程关闭时,我得到如下输出:

B: 7320
B: 7321
A: 4278 // What?
A: 7323
A: 7324
Run Code Online (Sandbox Code Playgroud)

返回输出位置AB关闭:

A: 4275
A: 4276
A: 4277
B: 2279 // ROBBLE ROBBLE!
B: 4279
B: 4280
Run Code Online (Sandbox Code Playgroud)

我可以看到A早先在4277处离开并在跳过7322并继续7323之前在4278处上升.这种情况发生在两者value++++value.我的理解是,在让任何其他线程执行该方法之前,synchronized让每个线程的方法调用getNext()完成.

当我使用时private volatile int value;,threadsafe.java我得到类似于以下的输出:

A: 8511
A: 8512
A: 8513
B: 7022 // Meh.
B: 8514
B: 8515
Run Code Online (Sandbox Code Playgroud)

这次没有跳过任何数字(从中跳过++value,仍然跳过value++),但输出似乎仍在使用缓存值.

我在这里俯瞰什么?

van*_*nza 6

你的ThreadSafe课很好.这很可能是因为线程调度.即,在第一个例子中,线程"A"在从实例获取4278之后但在将其打印到终端之前被解除调度.

那么,"B"跑了一会儿,取所有值高达7322,而被取消调度之前打印高达7321的所有值,此时"A"再次安排,它的下一个步骤是打印有值先前获取(4278),并继续从ThreadSafe类(7323)的下一个值.

所以你的问题是,虽然获取数字是正确的线程安全,但打印它们不是.

  • @KPThunder:最简单的方法是在System.out.println调用周围放置一个`synchronized(ts){}`块.这样你的`getNext()`方法也不需要同步.或者将print调用移动到`getNext()`方法(在这种情况下,它会变成"get and print"). (2认同)