原始代码
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 实现方法 int count(String ip) 返回ip访问次数
*/
public class IpCount {
static volatile ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
static final Object o = new Object();
public static int count(String ip) {
if (count.keySet().contains(ip)) {
return count.get(ip).incrementAndGet();
}
synchronized (o) {
if (count.keySet().contains(ip)) {
return count.get(ip).incrementAndGet();
} else {
count.put(ip, new AtomicInteger(1));
return 1;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10000];
Random r = new Random();
IpCount ipCount = new IpCount();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int ip = r.nextInt(5)+1;
int a = ipCount.count(Integer.toString(ip));
}, "t" + i);
}
for (Thread t : threads) {
t.start();
}
Thread.sleep(5);
System.out.println(ipCount.count);
int sum = 0;
for (AtomicInteger value : ipCount.count.values()) {
sum +=value.get();
}
System.out.println(sum);
}
}
运行结果
- 思路分析
- 首先看到这些代码时,synchronized与Atomic同时使用
- 也就是说,锁升级与CAS同时使用
- Volatile的作用是 防止指令重排序和线程可见性
- ConcurrentHashMap当调用时不存在出现半初始话状态
- 这种极短的操作使用原子操作确实好很多,第一想法是synchronized去掉
- 静态方法也没必要
去掉之后
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 实现方法 int count(String ip) 返回ip访问次数
*/
public class IpCount {
ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
public int count(String ip) {
if (count.keySet().contains(ip)) {
return count.get(ip).incrementAndGet();
} else {
count.put(ip, new AtomicInteger(1));
return 1;
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10000];
Random r = new Random();
IpCount ipCount = new IpCount();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int ip = r.nextInt(5)+1;
int a = ipCount.count(Integer.toString(ip));
}, "t" + i);
}
for (Thread t : threads) {
t.start();
}
Thread.sleep(5);
System.out.println(ipCount.count);
int sum = 0;
for (AtomicInteger value : ipCount.count.values()) {
sum +=value.get();
}
System.out.println(sum);
}
}
确实是出了一些问题,在意料之中
- 到这里其实出现了沉思ConcurrentHashMap本身是线程安全的,为什么contains()会出现不安全了
- 突然发现count.keySet().contains(ip)多出了一个keySet()
- 尝试用count.containsKey(ip)替换count.keySet().contains(ip),也不正常
ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
public int count(String ip) {
if (count.containsKey(ip)) {
return count.get(ip).incrementAndGet();
} else {
count.put(ip, new AtomicInteger(1));
return 1;
}
}
putIfAbsent()
但如果使用count.putIfAbsent()还会更优
- 如何没有key对应的值,put
- 如果已存在值依旧是原来的值,不做修改
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 实现方法 int count(String ip) 返回ip访问次数
*/
public class IpCount {
ConcurrentHashMap<String, AtomicInteger> count = new ConcurrentHashMap<>();
public int count(String ip) {
count.putIfAbsent(ip, new AtomicInteger(0));
return count.get(ip).incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10000];
Random r = new Random();
IpCount ipCount = new IpCount();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int ip = r.nextInt(5)+1;
int a = ipCount.count(Integer.toString(ip));
}, "t" + i);
}
for (Thread t : threads) {
t.start();
}
Thread.sleep(5);
System.out.println(ipCount.count);
int sum = 0;
for (AtomicInteger value : ipCount.count.values()) {
sum +=value.get();
}
System.out.println(sum);
}
}