import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
/*
* This program shows multi-threading only makes sense when the computation per thread is heavy.
* Creating thread is not free, sometimes, multi-threaded program could be slower than single thread
* */
public class AtomicAction1 {
// large number to average out JVM activities such as garbage collection,
// initialization, optimization
private static final int TOTALRUN = 200;
private static final int TOTALTHREAD = 400;
private static final int COUNT_PER_TREAD = 100;
private static long count = 0;
private static long totalTime = 0;
private static AtomicLong CNT = new AtomicLong(0);
// ordered thread safe set
private static Set<Long> bank = new ConcurrentSkipListSet<>();
// JVM will optimize during first a few runs, don't include them in
// statistics
// running the test in IDE such as eclipse and console will have different results, because IDE have max memory limits
private static final int DISCARDFIRSTFEW = 20;
public static void main(String... args) throws InterruptedException {
System.out.println("available core number: "
+ Runtime.getRuntime().availableProcessors());
totalTime = 0;
runTest("singleThread");
totalTime = 0;
runTest("multiThreadWithLock");
totalTime = 0;
runTest("multiThreadWithAtomic");
}
private static void runTest(String type) throws InterruptedException {
for (int i = 0; i < TOTALRUN; i++) {
init();
test(type, i);
}
System.out.println(type + " average time per run: "
+ (double) totalTime / (double) (TOTALRUN - DISCARDFIRSTFEW)
+ " miliseconds.");
}
private static void test(String type, int i) throws InterruptedException {
switch (type) {
case "singleThread":
long t = runSingleThread();
if (i >= DISCARDFIRSTFEW) {
totalTime += t;
}
break;
case "multiThreadWithAtomic":
t = runMultiThread(true);
if (i >= DISCARDFIRSTFEW) {
totalTime += t;
}
break;
case "multiThreadWithLock":
t = runMultiThread(false);
if (i >= DISCARDFIRSTFEW) {
totalTime += t;
}
break;
default:
System.out.println("Unknown type");
}
}
private static void init() {
count = 0;
CNT.set(0);
bank.clear();
}
private static long runSingleThread() {
long start = System.currentTimeMillis();
for (int i = 0; i < TOTALTHREAD * COUNT_PER_TREAD; i++) {
bank.add(count++);
}
return System.currentTimeMillis() - start;
}
private static long runMultiThread(boolean atomic)
throws InterruptedException {
long start = System.currentTimeMillis();
CountDownLatch latch = new CountDownLatch(TOTALTHREAD);
for (long i = 0; i < TOTALTHREAD; i++) {
Thread thread;
if (atomic) {
thread = new Thread(new AtomicRunnalbe(latch));
} else {
thread = new Thread(new SynchRunnable(latch));
}
thread.start();
}
latch.await();
return System.currentTimeMillis() - start;
}
private static class AtomicRunnalbe implements Runnable {
private CountDownLatch latch;
public AtomicRunnalbe(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
for (int i = 0; i < COUNT_PER_TREAD; i++) {
bank.add(CNT.getAndIncrement());
}
latch.countDown();
}
}
private static class SynchRunnable implements Runnable {
private CountDownLatch latch;
public SynchRunnable(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
for (int i = 0; i < COUNT_PER_TREAD; i++) {
synchronized (bank) {
bank.add(count++);
}
}
latch.countDown();
}
}
}