现在web程序的计数器的应用场景比较多,分布式计数器可以使用redis,但是作为java程序员就会考虑怎么使用单节点多线程实现一个计数器。
计数器有两个层次的要求
1,多个线程访问不会造成计数器数值丢失
2,是否对计数器返回的值有要求,比如打印,比如用这个值做些事情
如果要满足2的话,那只有一种方式就是加锁,把相应的代码块加锁
如果要满足1的话,有两种思路,一个使用Java的并发包里面的atomic类型,一种是使用volatile变量然后使用sychronized变量。
因此有了如下两个程序
程序1
package com.fb.concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
public class MutiThreadCount implements Callable<Long> {
private AtomicInteger ai = new AtomicInteger();
public MutiThreadCount() {
}
@Override
public Long call() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
ai.incrementAndGet();
}
Long end = System.currentTimeMillis();
return end - start;
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(1000);
Long maxtime = 0l;
MutiThreadCount mtc = new MutiThreadCount();
List<Future<Long>> result = new ArrayList<Future<Long>>();
for (int i = 0; i < 1000; i++) {
Future<Long> time = es.submit(mtc);
result.add(time);
}
es.shutdown();
for (Future<Long> f : result) {
Long time = f.get();
if (time > maxtime) {
maxtime = time;
}
}
System.out.println(maxtime);
System.out.println(mtc.ai.get());
}
}
程序2:
package com.fb.concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MutiThread1Count implements Callable<Long> {
private int counting;
@Override
public Long call() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
incr();
}
Long end = System.currentTimeMillis();
return end - start;
}
public synchronized void incr() {
counting++;
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
Long start = System.currentTimeMillis();
ExecutorService es = Executors.newFixedThreadPool(1000);
MutiThread1Count mtc1 = new MutiThread1Count();
List<Future<Long>> result = new ArrayList<Future<Long>>();
for (int i = 0; i < 1000; i++) {
Future<Long> time = es.submit(mtc1);
result.add(time);
}
es.shutdown();
Long maxtime = 0l;
Long total = 0l;
for (Future<Long> f : result) {
Long time = f.get();
if (time > maxtime) {
maxtime = time;
}
total += time;
}
System.out.println("avg time is :" + total / result.size());
System.out.println(maxtime);
System.out.println(mtc1.counting);
Long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
但是,在本机环境(intel I7 4核8线程)执行的时候,第一个程序可以吃到100%的cpu,第二个程序最多使用4个cpu线程,而且还不会到100%。
简单的理解就是:第一个程序不阻塞,全线程的跑,第二个线程synchronized会有可能导致每次都会重选进入锁的程序。
但是还是比较质疑这个想法:volatile变量使用比较脆弱,第二种调用效率按说应该比第一个高,参考:
www.ibm.com/developerworks/cn/java/j-jtp06197.html
为什么呢?