关于AtomicInteger变量的一些疑问

本人最近写了一段代码,用到了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());
    }

5个回答

先说最早的版本:
// 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;
    }
u011606457
_1_1_7_ 你这问题有悬赏分吗?没看到,不过没关系,我也是涨见闻来的
大约 3 年之前 回复
lcx_1989210
lcx_1989210 谢谢你了,你的这个方案采纳了。是不是分就给你了呢?
大约 3 年之前 回复

逻辑就很大问题,全局变量和局部变量也没划分清楚,
特别: Long currentTime = (System.currentTimeMillis() - baseTime);
这个时间基本每次都不一样了,还需要缓存在idMap吗?

u011606457
_1_1_7_ ReentrantLock,ConcurrentHashMap在这里都是画蛇添足,且用法不当,导致了该问题
大约 3 年之前 回复
u011606457
_1_1_7_ 回复lcx_1989210: 是有问题,但不是AtomicInteger的问题,是你用法的问题
大约 3 年之前 回复
lcx_1989210
lcx_1989210 回复_1_1_7_: 不是吧,我这边用了AtomicInteger后,在currentTime一样的情况下,打印出的queueId还是会有重复的呢。
大约 3 年之前 回复
u011606457
_1_1_7_ 回复lcx_1989210: 你把问题复杂化,引来了不必要的麻烦和问题,AtomicInteger本身就可以保持原子性,多线程安全;代码我是运行过的,所以才那样评价的
大约 3 年之前 回复
lcx_1989210
lcx_1989210 这段代码最好运行一下,现在的问题就是currentTime的值一样,queueId在并发的情况下也会有重复。也就是说在毫秒级别内也有重复数据,现在就想解决这个重复数据的问题。现在加了lock锁的时候还是不行。
大约 3 年之前 回复

我简化了代码:

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了

lcx_1989210
lcx_1989210 你这个方法我本地刚跑过了,你这样的测试确实没问题的。但是我现在很疑问的就是为什么加上if的一些逻辑之后就变得不可预见了,我在你这个基础上改了下代码,测试结果还是会有queueId重复的情况。
大约 3 年之前 回复

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的值。

![图片说明](https://img-ask.csdn.net/upload/201606/28/1467085304_869998.png)
lcx_1989210
lcx_1989210 我现在特别纳闷的就是为什么queueId会重复呢??
大约 3 年之前 回复

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

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!