当我遇到这种情况时,如何对情况进行建模?是否因同时执行的线程而损坏?

发布于 2024-11-29 00:10:08 字数 272 浏览 0 评论 0原文

如果 int 递增/递减操作在 Java 6 中不是原子的,也就是说,它们是分几个步骤执行的(读取值、递增、写入等),我希望看到一段代码来演示如何执行多个步骤线程可以影响单个 int 变量,从而导致其完全损坏。

例如,基本步骤包括但不涵盖所有这些: i++ ~= 将 i 放入寄存器;增加 i(包括 asm 操作);把我写回记忆中;

如果两个或多个线程在此过程中交错,这可能意味着两次后续调用 i++ 后的值将仅增加一次。

你能用java演示一段在多线程环境中模拟这种情况的代码吗?

If int incrementing/decrementing operations are not atomic in Java 6, that is, they are said to be performed in several steps (read the value, increment, write etc), I would like to see a piece of code that will demonstrate how multiple threads can affect a single int variable in a way that will corrupt it entirely.

For example, elementary steps include, but not cover all, these:
i++ ~= put i to the register; increment i (inc asm operation); write i back to memory;

if two or more threads interleave during the process, that would probably mean that the value after two consequent calls to i++ will be incremented only once.

Can you demonstrate a piece of code in java that models this situation in multithreading environment?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ぽ尐不点ル 2024-12-06 00:10:09
public class Test {
    private static int counter;

    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            public void run() {
                for (int i = 0; i < 100000; i++) {
                    counter++;
                }
            }
        };
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        if (counter != 200000) {
            System.out.println("Houston, we have a synchronization problem: counter should be 200000, but it is " + counter);
        }
    }
}

在我的机器上运行这个程序给出

Houston, we have a synchronization problem: counter should be 200000, but it is 198459
public class Test {
    private static int counter;

    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            public void run() {
                for (int i = 0; i < 100000; i++) {
                    counter++;
                }
            }
        };
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        if (counter != 200000) {
            System.out.println("Houston, we have a synchronization problem: counter should be 200000, but it is " + counter);
        }
    }
}

Running this program on my machine gives

Houston, we have a synchronization problem: counter should be 200000, but it is 198459
寄居人 2024-12-06 00:10:09

这是代码。对于 static 表示抱歉,只是想节省几行代码,它不会影响结果:

public class Test
{
    public static int value;

    public static void main(String[] args) throws InterruptedException
    {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 50000; ++i)
                    ++value;
            }
        };

        List<Thread> threads = new ArrayList<Thread>();
        for(int j = 0; j < 2; ++j)
            threads.add(new Thread(r));
        for (Thread thread : threads)
            thread.start();
        for (Thread thread : threads)
            thread.join();

        System.out.println(value);
    }
}

该程序可以打印 50000 到 100000 之间的任何内容,但它从未在我的机器上实际打印过 100000。

现在将 int 替换为 AtomicIntegerincrementAndGet() 方法。它总是会打印 100000,而不会产生大的性能影响(它使用 CPU CAS 指令,没有 Java 同步)。

Here is the code. Sorry for the static, just wanted to save few lines of code, it does not affect the results:

public class Test
{
    public static int value;

    public static void main(String[] args) throws InterruptedException
    {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 50000; ++i)
                    ++value;
            }
        };

        List<Thread> threads = new ArrayList<Thread>();
        for(int j = 0; j < 2; ++j)
            threads.add(new Thread(r));
        for (Thread thread : threads)
            thread.start();
        for (Thread thread : threads)
            thread.join();

        System.out.println(value);
    }
}

This program can print anything between 50000 and 100000, but it never actually printed 100000 on my machine.

Now replace int with AtomicInteger and incrementAndGet() method. It will always print 100000 without big performance impact (it uses CPU CAS instructions, no Java synchronization).

や三分注定 2024-12-06 00:10:09

您需要运行多次迭代的测试,因为 ++ 速度很快,并且可以在出现问题之前运行完成。

public static void main(String... args) throws InterruptedException {
    for (int nThreads = 1; nThreads <= 16; nThreads*=2)
        doThreadSafeTest(nThreads);
}

private static void doThreadSafeTest(final int nThreads) throws InterruptedException {
    final int count = 1000 * 1000 * 1000;

    ExecutorService es = Executors.newFixedThreadPool(nThreads);
    final int[] num = {0};
    for (int i = 0; i < nThreads; i++)
        es.submit(new Runnable() {
            public void run() {
                for (int j = 0; j < count; j += nThreads)
                    num[0]++;
            }
        });
    es.shutdown();
    es.awaitTermination(10, TimeUnit.SECONDS);
    System.out.printf("With %,d threads should total %,d but was %,d%n", nThreads, count, num[0]);
}

打印

With 1 threads should total 1,000,000,000 but was 1,000,000,000
With 2 threads should total 1,000,000,000 but was 501,493,584
With 4 threads should total 1,000,000,000 but was 319,482,716
With 8 threads should total 1,000,000,000 but was 261,092,117
With 16 threads should total 1,000,000,000 but was 202,145,371

仅用 500K 我在一台基本笔记本电脑上得到了以下结果。在更快的机器上,您可以在发现问题之前拥有更高的迭代次数。

With 1 threads should total 500,000 but was 500,000
With 2 threads should total 500,000 but was 500,000
With 4 threads should total 500,000 but was 500,000
With 8 threads should total 500,000 but was 500,000
With 16 threads should total 500,000 but was 500,000

You need to run the test for many iterations as ++ is quick and can run to completion before there is time for there to be a problem.

public static void main(String... args) throws InterruptedException {
    for (int nThreads = 1; nThreads <= 16; nThreads*=2)
        doThreadSafeTest(nThreads);
}

private static void doThreadSafeTest(final int nThreads) throws InterruptedException {
    final int count = 1000 * 1000 * 1000;

    ExecutorService es = Executors.newFixedThreadPool(nThreads);
    final int[] num = {0};
    for (int i = 0; i < nThreads; i++)
        es.submit(new Runnable() {
            public void run() {
                for (int j = 0; j < count; j += nThreads)
                    num[0]++;
            }
        });
    es.shutdown();
    es.awaitTermination(10, TimeUnit.SECONDS);
    System.out.printf("With %,d threads should total %,d but was %,d%n", nThreads, count, num[0]);
}

prints

With 1 threads should total 1,000,000,000 but was 1,000,000,000
With 2 threads should total 1,000,000,000 but was 501,493,584
With 4 threads should total 1,000,000,000 but was 319,482,716
With 8 threads should total 1,000,000,000 but was 261,092,117
With 16 threads should total 1,000,000,000 but was 202,145,371

with only 500K I got the following on a basic laptop. On a faster machine you can have a higher iteration count before a problem would be seen.

With 1 threads should total 500,000 but was 500,000
With 2 threads should total 500,000 but was 500,000
With 4 threads should total 500,000 but was 500,000
With 8 threads should total 500,000 but was 500,000
With 16 threads should total 500,000 but was 500,000
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文