【问题标题】:How to reproduce race condition in java?如何在java中重现竞争条件?
【发布时间】:2014-05-15 16:05:34
【问题描述】:

我试图创建这样的竞争条件。

class Bankaccount {

    private int balance=101;

    public int getBalance(){
        return balance;
    }
    public void withdraw(int i){
        balance=balance-i;
        System.out.println("..."+balance);
    }
}


public class Job implements Runnable{

    Bankaccount b=new Bankaccount();

    public void run(){
        if(b.getBalance()>100){
            System.out.println("the balanced ammount is"+b.getBalance());
            /*try{
            Thread.sleep(9000);
            }
            catch(Exception e){

            }*/
            makeWithdrawl(100);
        }   
    }

    public void makeWithdrawl(int ammount){

        b.withdraw(ammount);
        System.out.println(b.getBalance());

    }
    public static void main(String[] args) {

        Job x=new Job();
        Job y=new Job();

        Thread t1=new Thread(x);
        Thread t2=new Thread(y);
        t1.start();
        t2.start();
    }

}

我得到输出: 余额为101 ...1 1 余额为101 ...1

我预计它会是负数,因为两次提款发生在 100 美元

这里缺少什么?提前致谢

【问题讨论】:

  • 您没有给线程足够的时间来应对竞争条件。它也可能随时发生。
  • println() 语句也会大大减慢速度。
  • 每个线程(作业)都使用自己的 BankAccount 实例,所以我看不到任何方法可以在这里创建竞争条件。

标签: java multithreading


【解决方案1】:

当多个线程更改共享数据时会出现竞争条件。在您的示例中,每个线程都有自己的 Bankaccount 类。您需要将其共享,如下所示:

class Job implements Runnable{

    Bankaccount b;

    Job(Bankaccount b){
        this.b = b;
    }

    public void run(){
        if (b != null)
            if(b.getBalance()>100){
                System.out.println("the balanced ammount is " + b.getBalance());
                makeWithdrawal(100);
            }
    }

    public void makeWithdrawal(int ammount){
        b.withdraw(ammount);
        System.out.println(b.getBalance());
    }

    public static void main(String[] args) {

        // Creating one Bankaccount instance
        Bankaccount b = new Bankaccount();

        // Passing one instance to different threads 
        Job x=new Job(b);
        Job y=new Job(b);

        Thread t1=new Thread(x);
        Thread t2=new Thread(y);

        // Race conditions may appear 
        t1.start();
        t2.start();
    }

}

不幸的是,这还不够。多线程程序是非确定性的,您可以在程序执行多次后收到不同的结果。例如,线程 t1 可以在线程 t2 开始检查余额之前成功提款。因此,t2 不会因为缺钱而提现。

为了增加出现负余额的可能性,您可以在检查余额和取款之间插入延迟。

【讨论】:

    【解决方案2】:

    您需要了解几件事。

    1) 您在特定系统上的特定 JVM 可能不受您试图在此处重现的竞争条件的影响。
    2)您不可能通过单次运行重现竞争条件。 应该是不确定的,如果它给出一致的结果,它就不是竞争条件,而是一个错误。为了提高您的机会,请进行自动健全性检查并运行代码 100k 次。 3) 使用内存屏障使两个线程同时启动会增加竞争条件发生的机会。使用多核系统也有帮助。 4) 你的代码无论如何都不能产生竞争条件。仔细观察 - 每个工作都使用它自己的帐户。对于竞争条件,您需要共享状态。

    【讨论】:

      【解决方案3】:

      您的代码无法创建竞争条件,但这里有一些信息供您参考。

      可靠地重现竞争条件将非常困难,因为多线程程序本质上是非确定性的。这意味着独立线程中独立命令的执行顺序没有严格的顺序。

      这个讨论有一些关于这个话题的好信息:

      Can a multi-threaded program ever be deterministic?

      我认为您在示例中的意思是,您希望余额在线程执行后具有特定值。为此,您必须使用锁来确保唯一的一个线程一次访问相关变量。

      锁确保任何线程在其他线程正在操作该值时尝试读取该值,必须等到操作该变量的线程完成,然后才能读取和使用该变量本身。

      您将需要锁来执行您在示例中尝试执行的操作


      【讨论】:

      • 我把run()里面的所有代码都放到了synchronized(this)里面,结果还是一样?
      • 不要说“复制”,而是说“演示”。设置数据竞赛很容易。你可以在文献和网络上找到很多例子。困难的部分是证明比赛的结果并不总是一样的。当我不得不在我公司的生产代码中演示数据竞赛时,我运行了 1000 万次实验,结果错误的方法只有不到 100 次。
      • 另外我想我应该注意,你必须为两个线程使用相同的银行帐户实例,如果你给每个线程自己的银行帐户实例,就像提到的那样,你不能得到竞争条件,
      • @Rajesh,正如用户 'home' 在 cmets 中就您的问题指出的那样,如果您继续将 x 传递给一个线程,将 y 传递给另一个线程,则两个线程都无法访问同一家银行帐户。你需要让他们使用同一个银行账户
      • @james large,您说得对,因为设置数据竞赛很容易。确定结果是困难的部分。然而,OP 似乎在问是什么让“比赛条件再次发生”,所以我使用了重现
      【解决方案4】:

      尝试使用此代码生成竞态条件

      // This class exposes a publicly accessible counter
      // to help demonstrate data race problem
      class Counter {
          public static long count = 0;
      }
      
      // This class implements Runnable interface
      // Its run method increments the counter three times
      class UseCounter implements Runnable {
          public void increment() {
              // increments the counter and prints the value
              // of the counter shared between threads
              Counter.count++;
              System.out.print(Counter.count + " ");
          }
      
          public void run() {
              increment();
              increment();
              increment();
          }
      }
      
      // This class creates three threads
      public class DataRace {
          public static void main(String args[]) {
              UseCounter c = new UseCounter();
              Thread t1 = new Thread(c);
              Thread t2 = new Thread(c);
              Thread t3 = new Thread(c);
              t1.start();
              t2.start();
              t3.start();
          }
      }
      

      并尝试使用此代码修复它

      public void increment() {
          // increments the counter and prints the value
          // of the counter shared between threads
          synchronized(this){
              Counter.count++;
              System.out.print(Counter.count + " ");
          }
      }
      

      这段代码 sn-p 来自于 SG Ganesh, Tushar Sharma 所著的《Oracle Certified Professional Java SE 7 Programmer Exams 1Z0-804 and 1Z0-805》一书

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-21
        • 1970-01-01
        • 1970-01-01
        • 2019-06-12
        相关资源
        最近更新 更多