本人最近写了一段代码,用到了AtomicInteger对这个变量,但是期待的结果与代码的实际运行
结果相差好大,现将代码奉上,求各位大牛给下解决方案。
class IDGeneration {
private final static ConcurrentHashMap idMap = new ConcurrentHashMap();
private static final long baseTime = 1462377600000L;
private Lock lock;
IDGeneration() {
lock = new ReentrantLock();
}
@SuppressWarnings("unused")
public Long generateRelativeIncrementUniqueId(String flag) {
String dateString = GengratedUnqueId.formatDate(new Date(),
GengratedUnqueId.PATTERN);
Long currentTime = (System.currentTimeMillis() - baseTime);
AtomicInteger queueId = new AtomicInteger(0);
int i = 0;
Long tempValue = null;
lock.lock();
try {
queueId = idMap.get(currentTime);
if (queueId == null) {
queueId = new AtomicInteger(0);
idMap.clear();
idMap.put(currentTime, queueId);
tempValue = (currentTime << 5 | queueId.get());
} else {
queueId.getAndIncrement();
idMap.put(currentTime, queueId);
tempValue = (currentTime << 5 | queueId.get());
}
} finally {
lock.unlock();
}
return tempValue;
return null;
}
}
这是测试代码:
public static void main(String[] args) throws Exception {
static ConcurrentHashMap<Long, String> map = new ConcurrentHashMap<Long, String>();
final IDGeneration idGeneration = new IDGeneration();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
long value = idGeneration
.generateRelativeIncrementUniqueId();
map.put(value, "");
}
});
thread.start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println("map.size===" + map.size());
}
先说最早的版本:
// idMap.clear();
这行代码注释掉就可以了,少的部分其实是被你自己清除了,同步上是够了;
后面你改的,缺少同步,多线程不安全,改如下:
public Long generateRelativeIncrementUniqueId() {
long tempValue = (System.currentTimeMillis() - baseTime);
long value = 0;
AtomicInteger queueId = map.get(tempValue);
if (queueId == null) {
// 这里需要同步,因为map中还没有queueId
synchronized (this) {
// 需要再检验一遍
queueId = map.get(tempValue);
if (queueId == null) {
queueId = new AtomicInteger(0);
map.put(tempValue, queueId);
}
}
value = (tempValue << 8 | queueId.getAndIncrement());
} else {
// queueId.getAndIncrement();
// map.put(tempValue, queueId);//没必要
value = (tempValue << 8 | queueId.getAndIncrement());
}
System.out.println(tempValue + "," + queueId.get());
return value;
}
逻辑就很大问题,全局变量和局部变量也没划分清楚,
特别: Long currentTime = (System.currentTimeMillis() - baseTime);
这个时间基本每次都不一样了,还需要缓存在idMap吗?
我简化了代码:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class SimpleID {
private static final long baseTime = 1462377600000L;
public static void main(String[] args) throws Exception {
final ConcurrentHashMap<Long, String> map = new ConcurrentHashMap<Long, String>();
final SimpleID idGeneration = new SimpleID();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
long value = idGeneration.generateRelativeIncrementUniqueId();
map.put(value, "");
}
});
thread.start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println("map.size===" + map.size());
}
private AtomicInteger queueId = new AtomicInteger(0);
public Long generateRelativeIncrementUniqueId() {
Long tempValue = baseTime + queueId.getAndIncrement();
return tempValue;
}
}
AtomicInteger足够了,它本身是没问题的,这样你就可以好好理解AtomicInteger了
public Long generateRelativeIncrementUniqueId() {
long tempValue = (System.currentTimeMillis() - baseTime);
long value = 0;
queueId = map.get(tempValue);
if (queueId == null) {
queueId = new AtomicInteger(0);
map.put(tempValue, queueId);
value = (tempValue << 8 | queueId.get());
} else {
queueId.getAndIncrement();
map.put(tempValue, queueId);
value = (tempValue << 8 | queueId.get());
}
System.out.println(tempValue + "," + queueId.get());
return value;
}
这是处理方法的逻辑,main方法保持不变。运行结果后:map.size===95
下边的图片是几个重复的queueId的值。

这是图片的部分重复数据的内容:
4707077338,0
4707077338,0
4707077338,2
4707077338,1
4707077339,0
4707077339,2
4707077339,2