mvg*_*mvg 5 java multithreading
我是Java多线程的新手.我正在学习种族条件的概念.
基于Oracle文档
http://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html
我创建了一个示例代码,如下所示
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;
}
}
Run Code Online (Sandbox Code Playgroud)
该代码不显示任何比赛条件.你能帮助我,如何使用上面的代码刺激竞争条件?
谢谢
为了在两个线程之间进行竞争,这两个线程之间必须存在共享状态,并且该状态的交互(读取和写入)必须在mutualy独占块(也称为同步)之外发生.读取,递增然后写回同步块之外的易失性字段就是一个很好的例子.
例如,请考虑此博客中记录的这种情况.
线程A和B都可以在发生任何修改之前读取计数器.然后他们都增加,然后他们都写.结果将是18,而不是19.因为它已经是19,我们将需要线程B来读取线程A写入计数器后的计数器.哪个,有时可能发生.这就是它被称为种族的原因.

为了可靠地实现这种竞争,请更改上面的测试代码以在线程外部创建计数器,然后通过其构造函数将其传递给它们.
你遇到的第二个问题是重叠操作的窗口是非常好的,并且考虑到启动一个线程,相比之下,很多头部比较,那么这三个线程在恰当的时间内重叠的可能性非常大低.因此,为了增加他们的几率,你应该在一个紧密的循环中重复运行.
以下代码演示了上述两个概念.所做的更改是:
.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,只是为了好玩; 将synchronized同步添加到MyCounter的increment方法,然后重新运行.竞争条件将消失,现在程序将正确打印2000000.这是因为每次增加调用现在只允许一个线程一次进入共享方法.因此序列化对共享变量c的每次访问,并结束竞争.
最简单的竞争条件是两个线程使用此模式更新某些共享数据
read a value
think for a bit, giving another thread a chance to get in
increment the value and write it back
Run Code Online (Sandbox Code Playgroud)
所以现在如果你有两个线程在运行,每个线程递增一个初始值为43的计数器,我们期望这样
A reads value 43
A thinks
A increments and writes 44
B reads value 44
B thinks
B increments and writes 45
Run Code Online (Sandbox Code Playgroud)
但这可能发生,因为"思考窗口"
A reads value 43
A thinks
B reads value (it's still) 43
B thinks
B increments 43 to 44 and writes
A increments 43 to 44 and write
// the value is now 44, and we expected it to be 45
Run Code Online (Sandbox Code Playgroud)
竞赛的关键思想是你会得到意想不到的不良影响,例如在库存应用程序中,两个线程各自减少库存量,就像上面的例子中我们"丢失"其中一个减量.
现在您的代码有两个问题:
1).没有共享值,所以我们没有机会看到任何这样的争论
2).你在一行代码中递增一个整数,因此两个线程碰撞的可能性很小.在模拟比赛中,如上所示,最好将读写分开,然后通过睡眠创建一个"机会之窗"来模拟思考时间.在多处理器环境中,线程可能真正并行运行,即使单行代码可能会出现竞争,因为JVM内部会进行读写操作,甚至可能保留值的缓存.