How to model a situation, when i++ is corrupted by simultaneously executing threads?
If int incrementing/decrementing operations are not atomic in Java 6, that is, they are said to be performed in several steps (read the value, increment, write etc), I would like to see a piece of code that will demonstrate how multiple threads can affect a single int variable in a way that will corrupt it entirely.
For example, elementary steps include, but not cover all, these: i++ ~= put i to开发者_如何转开发 the register; increment i (inc asm operation); write i back to memory;
if two or more threads interleave during the process, that would probably mean that the value after two consequent calls to i++ will be incremented only once.
Can you demonstrate a piece of code in java that models this situation in multithreading environment?
public class Test {
private static int counter;
public static void main(String[] args) throws InterruptedException {
Runnable r = new Runnable() {
public void run() {
for (int i = 0; i < 100000; i++) {
counter++;
}
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
if (counter != 200000) {
System.out.println("Houston, we have a synchronization problem: counter should be 200000, but it is " + counter);
}
}
}
Running this program on my machine gives
Houston, we have a synchronization problem: counter should be 200000, but it is 198459
Here is the code. Sorry for the static
, just wanted to save few lines of code, it does not affect the results:
public class Test
{
public static int value;
public static void main(String[] args) throws InterruptedException
{
Runnable r = new Runnable() {
@Override
public void run() {
for(int i = 0; i < 50000; ++i)
++value;
}
};
List<Thread> threads = new ArrayList<Thread>();
for(int j = 0; j < 2; ++j)
threads.add(new Thread(r));
for (Thread thread : threads)
thread.start();
for (Thread thread : threads)
thread.join();
System.out.println(value);
}
}
This program can print anything between 50000 and 100000, but it never actually printed 100000 on my machine.
Now replace int
with AtomicInteger
and incrementAndGet()
method. It will always print 100000 without big performance impact (it uses CPU CAS instructions, no Java synchronization).
You need to run the test for many iterations as ++
is quick and can run to completion before there is time for there to be a problem.
public static void main(String... args) throws InterruptedException {
for (int nThreads = 1; nThreads <= 16; nThreads*=2)
doThreadSafeTest(nThreads);
}
private static void doThreadSafeTest(final int nThreads) throws InterruptedException {
final int count = 1000 * 1000 * 1000;
ExecutorService es = Executors.newFixedThreadPool(nThreads);
final int[] num = {0};
for (int i = 0; i < nThreads; i++)
es.submit(new Runnable() {
public void run() {
for (int j = 0; j < count; j += nThreads)
num[0]++;
}
});
es.shutdown();
es.awaitTermination(10, TimeUnit.SECONDS);
System.out.printf("With %,d threads should total %,d but was %,d%n", nThreads, count, num[0]);
}
prints
With 1 threads should total 1,000,000,000 but was 1,000,000,000
With 2 threads should total 1,000,000,000 but was 501,493,584
With 4 threads should total 1,000,000,000 but was 319,482,716
With 8 threads should total 1,000,000,000 but was 261,092,117
With 16 threads should total 1,000,000,000 but was 202,145,371
with only 500K I got the following on a basic laptop. On a faster machine you can have a higher iteration count before a problem would be seen.
With 1 threads should total 500,000 but was 500,000
With 2 threads should total 500,000 but was 500,000
With 4 threads should total 500,000 but was 500,000
With 8 threads should total 500,000 but was 500,000
With 16 threads should total 500,000 but was 500,000
精彩评论