首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ConcurrentBitSet中的竞赛条件

ConcurrentBitSet中的竞赛条件
EN

Stack Overflow用户
提问于 2016-07-11 21:26:05
回答 1查看 168关注 0票数 3

我试图在java中创建并发位集,它允许扩展大小(相对于固定长度而言,这非常简单)。下面是类的核心部分(其他方法现在并不重要)

代码语言:javascript
复制
public class ConcurrentBitSet {

static final int CELL = 32;
static final int MASK = CELL - 1;
final AtomicReference<AtomicIntegerArray> aiaRef;

public ConcurrentBitSet(int initialBitSize) {
    aiaRef = new AtomicReference<>(new AtomicIntegerArray(1 + ((initialBitSize - 1) / CELL)));
}

public boolean get(int bit) {
    int cell = bit / CELL;
    if (cell >= aiaRef.get().length()) {
        return false;
    }
    int mask = 1 << (bit & MASK);
    return (aiaRef.get().get(cell) & mask) != 0;
}

public void set(int bit) {
    int cell = bit / CELL;
    int mask = 1 << (bit & MASK);
    while (true) {
        AtomicIntegerArray old = aiaRef.get();
        AtomicIntegerArray v = extend(old, cell);
        v.getAndAccumulate(cell, mask, (prev, m) -> prev | m);
        if (aiaRef.compareAndSet(old, v)) {
            break;
        }
    }
}

private AtomicIntegerArray extend(AtomicIntegerArray old, int cell) {
    AtomicIntegerArray v = old;
    if (cell >= v.length()) {
        v = new AtomicIntegerArray(cell + 1);
        for (int i = 0; i < old.length(); i++) {
            v.set(i, old.get(i));
        }
    }
    return v;
}

public String toString() {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < aiaRef.get().length(); i++) {
        for (int b = 0; b < CELL; b++) {
            sb.append(get(i * CELL + b) ? '1' : '0');
        }
    }
    return sb.toString();
}

}

不幸的是,这里似乎有比赛条件。

下面是示例测试代码,它每次都失败了几个位--它应该打印到第300位之前的所有一个,但是每次在不同的地方都没有随机零。一台PC我得到的只是少数,在另一台电脑上有8-10个零位的奇数/偶数位置。

代码语言:javascript
复制
    final ConcurrentBitSet cbs = new ConcurrentBitSet(10);
    CountDownLatch latch = new CountDownLatch(1);
    new Thread() {
        public void run() {
            try {
                latch.await();
                for (int i = 0; i < 300; i += 2) {
                    cbs.set(i);
                }
            } catch (InterruptedException e) {

            }
        };
    }.start();
    new Thread() {
        public void run() {
            try {
                latch.await();
                for (int i = 0; i < 300; i += 2) {
                    cbs.set(i + 1);
                }
            } catch (InterruptedException e) {
            }
        };
    }.start();
    latch.countDown();
    Thread.sleep(1000);
    System.out.println(cbs.toString());

我得到的11111111111111111111111111111111111111111111111111111101111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111100000000000000000000 11111111111111111111111111111111111111110111111111111111111111110101111111111111111111110101011111111111111111111111111111111111010101111111111111111111111111111111111101010111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000的例子

调试是很困难的(因为任何复杂的东西都会使争用条件消失),但看起来就像两个线程同时尝试扩展数组的大小一样,一个线程失败了,不得不重试they循环,结果是循环下一部分的aiaRef.get()数据损坏了--它已经访问了这个部分(就像试图扩展它的其他线程一样),结果里面有一些零。

有人知道窃听器在哪里吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-07-11 22:03:51

问题是,aiaRef.compareAndSet()只是在防范并发替换,因为AtomicReference的唯一任务是保护其引用的原子性。如果引用对象在我们重建数组时被并发修改,compareAndSet()将成功,因为它将相同的引用与其自身进行比较,但它可能忽略了一些修改。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38316491

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档