如何使范围树实现线程安全

时间:2015-12-22 22:31:19

标签: java algorithm concurrency binary-tree graph-theory

我实现了一个范围树,它以递增或递减特定值的计数的形式支持更新。它还可以查询低于或等于所提供值的值的数量。

范围树已经过测试,可以在单线程环境中工作,但我想知道如何修改实现,以便可以同时更新和查询。

我知道一个简单的解决方案是同步访问这个树的方法,但我想知道是否有办法使RangeTree线程本身安全,对性能的影响最小。

public class RangeTree {

    public static final int ROOT_NODE = 0;

    private int[] count;
    private int[] min;
    private int[] max;

    private int levels;
    private int lastLevelSize;

    public RangeTree(int maxValue) {

        levels = 1;
        lastLevelSize = 1;
        while (lastLevelSize <= maxValue) {
            levels++;
            lastLevelSize = lastLevelSize << 1;
        }

        int alloc = lastLevelSize * 2;
        count = new int[alloc];
        min = new int[alloc];
        max = new int[alloc];

        int step = lastLevelSize;
        int pointer = ROOT_NODE;
        for (int i = 0; i < levels; i++) {
            int current = 0;
            while (current < lastLevelSize) {
                min[pointer] = current;
                max[pointer] = current + step - 1;
                current += step;
                pointer++;
            }
            step = step >> 1;
        }
    }

    public void register(int value) {
        int index = lastLevelSize - 1 + value;
        count[index]++;

        walkAndRefresh(index);
    }

    public void unregister(int value) {
        int index = lastLevelSize - 1 + value;
        count[index]--;

        walkAndRefresh(index);
    }

    private void walkAndRefresh(int node) {
        int currentNode = node;
        while (currentNode != ROOT_NODE) {
            currentNode = (currentNode - 1) >> 1;
            count[currentNode] = count[currentNode * 2 + 1] + count[currentNode * 2 + 2];
        }
    }

    public int countLesserOrEq(int value) {
        return countLesserOrEq0(value, ROOT_NODE);
    }

    private int countLesserOrEq0(int value, int node) {
        if (max[node] <= value) {
            return count[node];
        } else if (min[node] > value) {
            return 0;
        }
        return countLesserOrEq0(value, node * 2 + 1) + countLesserOrEq0(value, node * 2 + 2);
    }
}

1 个答案:

答案 0 :(得分:1)

路易斯·瓦瑟曼是对的,这是一个棘手的问题。但它可能有简单的解决方案。
根据您的更新/读取比率和数据争用,可能对于使用ReadWriteLock而非同步非常有用。 在某些情况下可能高效的另一种解决方案(取决于您的工作负载)是在更新之前复制整个RangeTree对象,然后将引用切换为“实际”{{1 }}。就像它在CopyOnWriteArrayList中完成的那样。但这也违反了原子一致性协议,并使我们最终保持一致。