用于模拟Java线程中的竞争条件的代码 [英] Code to simulate race condition in Java thread

查看:77
本文介绍了用于模拟Java线程中的竞争条件的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Java多线程的新手。我正在学习竞争条件的概念。

I am new to Java multithreading. I am learning the concept of race condition.

基于Oracle文档

http://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html

我创建了一个示例代码,如下所示

I created a sample code like the one below

public class CounterTest {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new CounterIncThread());
        thread1.setName("add thread");
        thread1.start();

        Thread thread2 = new Thread(new CounterDecThread());
        thread2.setName("sub thread");
        thread2.start();

        Thread thread3 = new Thread(new CounterIncThread());
        thread3.setName("add thread2");
        thread3.start();
    }

}


class CounterIncThread implements Runnable {
    public void run() {
        SynchronizedCounter counter = new SynchronizedCounter();
        counter.increment();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String threadName =
                Thread.currentThread().getName();
        System.out.println(threadName+ ": "+counter.value());
    }
}

class CounterDecThread implements Runnable {
    public void run() {
        SynchronizedCounter counter = new SynchronizedCounter();
        counter.decrement();
        String threadName =
                Thread.currentThread().getName();
        System.out.println(threadName+ ": "+counter.value());
    }
}

class SynchronizedCounter {
    private int c = 0;

    public  void increment() {
        c++;
    }

    public   void decrement() {
        c--;
    }

    public  int value() {
        return c;
    }

}

代码不显示任何比赛条件。能帮助我,如何使用上面的代码刺激竞争条件?

The code doesn't show any racing condition. Can you please help me, how to stimulate the race condition using the above code?

谢谢

推荐答案

为了在两个线程之间进行竞争,这两个线程之间必须存在共享状态,并且该状态的交互(读取和写入)必须在mutualy独占块(aka同步化)之外发生。读取,递增然后写回同步块之外的易失性字段就是一个很好的例子。

In order to have a race between two threads, there must be shared state between those two threads and interaction (reading and writing) to that state must occur outside of a mutualy exclusive block (aka syncronized). Reading, incrementing and then writing back to a volatile field outside of a synchronized block is a great example of this.

例如,请考虑此博客

在发生任何修改之前,线程A和B都可以读取计数器。然后他们都增加,然后他们都写。最终结果将是18,而不是19.因为它已经是19,我们将需要线程B来读取线程A写入计数器后的计数器。哪个,有时可能发生。这就是为什么它被称为比赛。

Both thread A and B could read the counter before any modification occurs. They then both increment, and they then both write. The end result will then be 18, and not 19. For it to have been 19, we would have needed thread B to read the counter AFTER thread A had written to the counter. Which, can happen sometimes. That is why it is called a race.

为了可靠地实现这种竞争,请更改上面的测试代码,在线程外部创建计数器,然后通过其构造函数将其传递给它们。

To reliably achieve this kind of race, change your test code above to create the counter outside of the threads and then pass it in to them via their constructors.

你遇到的第二个问题是重叠操作的窗口是非常好的,并且考虑到启动一个线程,相比之下很多头顶那么这三个线程在恰当的时间进行研磨的可能性非常低。因此,为了增加他们的赔率,你应该在一个紧密的循环中重复运行。

The second problem that you have is that the window for the operations to overlap is very fine, and given that starting a thread has, in comparison a lot of over head then the chances of these three threads over lapping at just the right time is very low. Thus to increase their odds, you should repeat the runs in a tight loop.

以下代码演示了上述两个概念。所做的更改是:

The following code demonstrates the two concepts above. The changes made have been:


  1. 重命名类以使其使用更清晰

  2. 分享了两个线程之间的MyCounter状态

  3. 每个线程内的紧密循环,调用增量1,000,000次

  4. 主线程现在阻塞使用join()等待要完成的两个线程,这将取代之前的Thread.sleep

  5. MyCounter中的计数器值c现在是volatile;这告诉JVM总是向共享内存寻找值,而不是通过在遇到之间将它保持在寄存器内来进行优化。为了让竞争更加糟糕,请关闭volatile并查看会发生什么:)

  6. 主循环然后通过打印出计数器的值来完成,该值应为2,000,000。但这不会是由于竞争对手在竞争激烈的柜台上发生的。

  1. renamed classes to make their use a little clearer
  2. shared the state of MyCounter between the two threads
  3. tight loop within each thread, calling increment 1,000,000 times
  4. the main thread now blocks using join() waiting for the two threads to complete, this replaces the Thread.sleep that you had earlier
  5. the counter value c in MyCounter is now volatile; this tells the JVM to always go out to shared memory for the value and not to optimise by keeping it within a register between encounters. to make the race much worse, take volatile off and see what happens :)
  6. the main loop then finishes by printing out the value of the counter, which should be 2,000,000. but it will not be due to the race that is going on over the volatile counter.

public class CounterTest {    
    public static void main(String[] args) throws InterruptedException {   
        MyCounter counter = new MyCounter();

        Thread thread1 = new Thread(new CounterIncRunnable(counter));
        thread1.setName("add thread");
        thread1.start();

        Thread thread2 = new Thread(new CounterIncRunnable(counter));
        thread2.setName("add thread2");
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(counter.value());
    }    
}


class CounterIncRunnable implements Runnable {
    private MyCounter counter;

    public CounterIncRunnable(MyCounter counter) {
        this.counter = counter;
    }

    public void run() {
        for ( int i=0; i<1000000; i++ ) {
            counter.increment();
        }
    }
}


class MyCounter {
    private volatile int c = 0;

    public  void increment() {
        c++;
    }

    public   void decrement() {
        c--;
    }

    public  int value() {
        return c;
    }    
}

最后,只是为了好玩;将synchronized同步添加到MyCounter的increment方法,然后重新运行。竞争条件将消失,现在程序将正确打印2000000.这是因为每次增加调用现在只允许一个线程进入共享方法。因此序列化对共享变量c的每次访问,并结束比赛。

Finally, just for fun; add synchronized to the increment method of MyCounter and then rerun. The race condition will disappear, and now the program will correctly print 2000000. This is because every call to increment will now only allow one thread in to the shared method at a time. Thus serializing each access to the shared variable c, and putting an end to the race.

这篇关于用于模拟Java线程中的竞争条件的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆