0

我的问题是:

我有课:

public class AtomicStringBuilder {
    private final AtomicReference<StringBuilder> sbRef;
}

我需要同时原子地向 StringBuilder 添加新字符。但问题是,这个对象中只能有最后 128 个字符。我不能使用 StringBuffer,因为操作应该是非阻塞的。

所以,有两个操作:

首先:检查 StringBuilder 是否已经有 128 个字符。

第二:如果没有 -> 添加新字符,如果有 -> 删除第一个字符并添加新字符。

有没有办法使这两个或三个操作原子化?

我做了这个方法,但它不起作用:

public void append(String string) {
        this.sbRef.getAndUpdate(ref -> {
            if (ref.length() < 128) {
                ref.append(string);
            } else {
                ref.append(string).delete(0, ref.length() - 128);
            }
            return ref;
        });
    }

为了测试我创建了这个方法:

public void test() {
AtomicStringBuilder atomicStringBuilder = new AtomicStringBuilder();
Random random = new Random();
Stream<Integer> infiniteStream = Stream.iterate(0, i -> random.nextInt(10));

infiniteStream.parallel()
.limit(100000)
.forEach(integer -> atomicStringBuilder.append(String.valueOf(integer)));

assertEquals(128, atomicStringBuilder.getSb().get().length());
}

这不是一个真正的问题,我可以用其他任何可行的方法来更改 AtomicReference。任务是创建无锁且没有竞争条件的操作

4

1 回答 1

3

这是一个不可变字符串的解决方案。

如果你使用AtomicReference你需要返回一个新的引用而不是改变引用指向的对象。以原子方式比较引用的当前值和预期值是知道它没有被另一个线程更新的唯一方法。

getAndUpdate做这个:

  1. 获取当前参考
  2. 将 lambda 应用于引用,获得新的引用
  3. 如果当前引用没有改变,则自动将其设置为新引用,否则返回 1。
public class App {
    static class AtomicStringBuilder {
        public final AtomicInteger counter = new AtomicInteger();

        public final AtomicReference<String> sbRef = new AtomicReference<>("");

        public void append(String string) {
            this.sbRef.getAndUpdate(ref -> {
                counter.getAndIncrement();
                if (ref.length() < 128) {
                    return ref + string;
                } else {
                    String s = ref + string;
                    return s.substring(s.length() - 128);
                }
            });
        }
    }

    static void test() {
        AtomicStringBuilder atomicStringBuilder = new AtomicStringBuilder();
        Random random = new Random();
        Stream<Integer> infiniteStream = Stream.iterate(0, i -> random.nextInt(10));

        infiniteStream.parallel()
                .limit(100000)
                .forEach(integer -> atomicStringBuilder.append(String.valueOf(integer)));

        if (128 != atomicStringBuilder.sbRef.get().length()) {
            System.out.println("failed ");
        }
        System.out.println(atomicStringBuilder.sbRef.get());
        System.out.println(atomicStringBuilder.counter.get());
    }

    public static void main(String[] args) {
        test();
    }
}

我在 lambda 中添加了一个计数器。运行此程序后显示的值将超过 100,000,因为并发更新强制重试。

于 2021-12-13T07:20:36.743 回答