Java中的竞争条件及示例
时间:2020-01-09 10:35:07 来源:igfitidea点击:
当两个或者多个线程尝试访问共享资源时,Java中的竞争条件可能会以Java之类的多线程语言出现。如果所有线程都只是读取没有问题的共享对象,但是由于竞争条件,修改或者写入值可能会导致错误的结果。
在多线程环境中,执行几个步骤后的线程可能会被另一个线程抢占。这可能会使共享数据处于不一致状态。
例如,增加一个简单的任务-counter ++;
这个增加计数器的简单任务实际上包括三个步骤
- 读取计数器变量的值。
- 将值增加1.
- 存储计数器变量的值。
如果有两个线程共享此变量,则可能发生以下情况:
int counter = 0; counter = counter + 1; // Thread 1 counter = counter + 1; // Thread 2 started before thread 1 could save the new //value of counter, so Thread 2 also got the initial value of counter as 0. store counter value // Thread 1 store counter value // Thread 2
因此,由于线程交错,最终将计数器值设为1而不是正确的值2. 这就是竞争条件可以在多线程环境中对共享对象执行的操作。
由于比赛条件而导致的错误情况
由于存在竞争条件,执行线程可能会读取共享对象的陈旧值,这可能会导致以下任何情况。
- 如果线程必须根据变量的值执行某些逻辑。因为线程可能最终读取了错误的值,所以它可能无法按照预期的方式运行。这种情况称为先检查后竞争情况。
- 线程必须读取,修改和存储新值。再次由于竞争条件,线程可能最终读取和修改过时的值。这种情况称为读取-修改-写入竞争条件。
Java中竞争条件的示例
这是一个简单的示例,其中共享整数变量递增并显示值。创建十个线程,每个线程递增,然后显示变量的值。预期的行为是,每个线程应获得1-9之间的唯一值。
public class RaceConditionDemo { int counter = 0; public void incrementCounter(){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } counter++; } public int getCounter(){ return counter; } public static void main(String[] args) { RaceConditionDemo rc = new RaceConditionDemo(); for(int i = 0; i < 10; i++){ new Thread(new Runnable() { @Override public void run() { rc.incrementCounter(); System.out.println("value for " + Thread.currentThread().getName() + " - " + rc.getCounter()); } }).start(); } } }
输出:
value for Thread-0 - 1 value for Thread-2 - 2 value for Thread-1 - 3 value for Thread-4 - 4 value for Thread-5 - 6 value for Thread-3 - 6 value for Thread-6 - 6 value for Thread-9 - 8 value for Thread-8 - 9 value for Thread-7 – 8
在运行之一中,输出如上所述(请注意输出可能会有所不同)。如我们所见,线程5、3和6具有相同的值6,线程7和9也具有相同的值8.
避免Java中的竞争条件
现在,当我们知道什么是竞争条件并看到一个示例时,交织线程读取共享对象的相同值。这给我们带来了一个问题,即如何避免Java中的竞争条件。
显然,我们需要限制对关键部分的访问(修改共享资源的代码)。在Java中,这就是synced关键字的作用;同步对共享资源的访问。使用同步可确保原子操作作为单个操作执行而没有线程干扰。
在上面显示的示例中,同步方法调用应避免争用条件。
public class RaceConditionDemo { int counter = 0; public void incrementCounter(){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } counter++; } public int getCounter(){ return counter; } public static void main(String[] args) { RaceConditionDemo rc = new RaceConditionDemo(); for(int i = 0; i < 10; i++){ new Thread(new Runnable() { @Override public void run() { synchronized(rc){ rc.incrementCounter(); System.out.println("value for " + Thread.currentThread().getName() + " - " + rc.getCounter()); } } }).start(); } } }
输出:
value for Thread-0 - 1 value for Thread-8 - 2 value for Thread-7 - 3 value for Thread-9 - 4 value for Thread-6 - 5 value for Thread-4 - 6 value for Thread-5 - 7 value for Thread-3 - 8 value for Thread-2 - 9 value for Thread-1 – 10
如我们所见,每个线程都有一个唯一的值。