【问题标题】:Java multithreading Race condition scenarioJava 多线程竞争条件场景
【发布时间】:2017-11-25 17:25:32
【问题描述】:

我正在经历多线程中的竞态条件,并想创建一个导致竞态条件的情况,我为此编写了一个简单的程序,但每次都得到正确的结果。需要知道这是否是 Race 条件的正确情况。

代码如下:

package com.threads;

/**
* demonstrate race conditions
*/

public class Step4 {

public int getA() {
    return a;
}

public void addToA(int number) {

    for(int i=0;i<number;i++)
        this.a = this.a + 1;
}

int a = 2;
static Step4 s4 = new Step4();

public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(5);
        }
    });

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(3);
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(6);
        }
    });

    Thread thread4 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(4);
        }
    });


    thread1.start();    
    thread2.start();    
    thread3.start();
    thread4.start();

    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();

    System.out.println(s4.getA()); 

}

}

//在我的机器Win7 32bit中输出总是20。

【问题讨论】:

  • 很有可能每个线程在这里做的工作太少以至于它们根本不重叠。
  • 您正在等待所有 4 个线程完成(join() 方法),所以当然,一旦调用 println,内容总是 20。所以不,它不模拟比赛条件。
  • 使涉及的数字更大。竞争条件是不可预测的,因此让每个线程花费更多时间会增加它发生的机会。
  • 这是一个产生竞争条件的版本(至少在我的机器上):gist.github.com/jnizet/5a338e71282a249498ebc14736786c54

标签: java multithreading


【解决方案1】:

您的程序本身发生竞争条件的可能性非常低,因为所有线程都添加到字段 a,因此顺序确实会影响最终结果。 仅当两个线程读取相同的 a 值,然后首先分配一个新值,然后第二个分配一个新值,您可能会“丢失”一个增量。

这是一个更经常遭受竞争条件的修改版本:

public class Step4 {

  public int getA() {
    return a;
  }

  public void addToA(int number) {
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    for(int i=0;i<number;i++)
        this.a = this.a + 1;
  }

  public void multiplyA(int number) {
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    this.a = this.a * number;
  }

  int a = 2;
  static Step4 s4;

  public static void main(String[] args) throws InterruptedException {

    for (int i = 0; i < 10; i++) {
        s4 = new Step4();
        doRun();
        System.out.println("*******");
    }
  }

  private static void doRun() throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(5);
        }
    });

    Thread thread2 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.multiplyA(3);
        }
    });

    Thread thread3 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(6);
        }
    });

    Thread thread4 = new Thread(new Runnable() {
        @Override
        public void run() {
            s4.addToA(4);
        }
    });


    thread1.start();
    thread2.start();
    thread3.start();
    thread4.start();

    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();

    System.out.println(s4.getA());

  }

}

我为这两个动作添加了 100 毫秒的睡眠时间,以增加获得不同结果的机会。 大多数情况下,结果为 31,但有时可能为 17、21 或 51。

【讨论】:

  • 它确实受到竞争条件的影响。两个线程可以并行读取 a 的当前值,然后将其递增,因此您将失去递增。
【解决方案2】:

您的代码确实模拟了竞争条件,因为添加涉及读取和写入。但要真正展示这里的竞争性,你需要一些非常不幸的调度,因为所有线程所做的都是加法。您可能想尝试在另一台机器上运行它,或者让一些线程执行其他操作,例如除法或减法。

另一种选择是通过在addTo() 中添加Thread.sleep(random) 来确保竞争条件。

【讨论】:

    猜你喜欢
    • 2013-08-26
    • 1970-01-01
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-08
    • 1970-01-01
    相关资源
    最近更新 更多