当前位置: 首页 > 知识库问答 >
问题:

有没有一种方法可以让两个线程不从共享集合中获取相同的项?

翟冯浩
2023-03-14

我有一个程序有3个线程。一个线程专用于向共享集合中添加项,其余两个线程用于从集合中移除项并对其进行处理。

我创建了一个用于添加的Runnable。看起来是这样的:

public class Adder implements Runnable {

    private static final Integer MAX_NO = 1000; // I wanted this to be Integer.MAX_VALUE
    private final List<Integer> shared;
    private final AtomicBoolean isAddingDone;

    public Adder(final List<Integer> shared, final AtomicBoolean isAddingDone) {
        this.shared= shared;
        this.isAddingDone = isAddingDone;
    }

    @Override
    public void run() {
        for (int i = 0; i < MAX_NO; i++) {
            shared.add(i);
        }
        isAddingDone.set(true);
    }

}

我创建了另一个Runnable,该Runnable可从共享集合中删除项。

public class Remover implements Runnable {

    private final int id;
    private boolean stopRequested = false;
    private final List<Integer> shared;
    private final AtomicBoolean isAddingDone;

    public Remover(final int id, final List<Integer> shared, final AtomicBoolean isAddingDone) {
        this.id = id;
        this.shared = shared;
        this.isAddingDone = isAddingDone;
    }

    public synchronized void requestStop() {
        this.stopRequested = true;
    }

    public synchronized boolean isStopRequested() {
        return this.stopRequested;
    }

    @Override
    public void run() {
        System.out.println("Starting Remover" + id + "...");
        while (!isStopRequested()) {
            if (shared.isEmpty() && isAddingDone.get()) {
                this.requestStop();
            } else {
                final Integer value = shared.get(0);
                System.out.println("Remover" + this.id + " retrieved " + value + ". Removing...");
                shared.remove(value);
                // processing of value here
            }
        }
    }

}

主要方法如下:

public static void main(String[] args) {
    final List<Integer> shared = new Vector<>();
    final AtomicBoolean isAddingDone = new AtomicBoolean(false);

    final Runnable tAdder = new Adder(shared, isAddingDone);
    final Runnable tRemover0 = new Remover(0, shared, isAddingDone);
    final Runnable tRemover1 = new Remover(1, shared, isAddingDone);

    Thread t1 = new Thread(tAdder, "Thread-Adder");
    t1.start();

    Thread r0 = new Thread(tRemover0, "Thread-Remover0");
    r0.start();

    Thread r1 = new Thread(tRemover1, "Thread-Remover1");
    r1.start();
}

我在运行程序时没有错误,但我注意到,在某一点上,两个线程都在检索同一个项。

Starting Remover0...
Starting Remover1...
Remover1 retrieved 0. Removing...
Remover0 retrieved 0. Removing...
Remover1 retrieved 1. Removing...
Remover0 retrieved 1. Removing...
Remover1 retrieved 2. Removing...
Remover1 retrieved 3. Removing...

有没有一种方法可以让两个移除器不处理同一件物品?我尝试使用迭代器,但在运行时,我得到了一个ConcurrentModificationException。

共有1个答案

阎宾实
2023-03-14

shared.isempty()shared.get(0)shared.remove(value)不会自动发生,因此不能保证列表在其间没有更改。

如果要在线程之间通信,请使用blockingqueue,并使用poll()从其中删除可用的项。这允许您将IsEmpty()GetRemove组合到单个原子操作中。

// From the while loop inside Remover:
            Integer value = shared.poll();
            if (value == null /* indicates the queue was empty when you looked. */
                    && isAddingDone.get()) {
                this.requestStop();
            } else {
                System.out.println("Remover" + this.id + " retrieved " + value + ". Removing...");
                // No need to remove, you already did.
                // processing of value here
            }
 类似资料:
  • 我需要写一个私人的方法在java接收2数组。有没有办法使他们必须是相同的长度? 比如: 公共静态无效方法(int[]arr1,int[]arr2[arr1.length])

  • 我在Java写了一个游戏。我做了一个叫做Camera的类,女巫有两个静态的int表示相机的位置。摄像机的位置要求每一帧在许多不同的地方。这个位置有时也会在几个不同的地方被修改。我的问题是,我是否能够以一种使依赖的类独立和更可重用的方式分离camera类。我看过一些东西,包括监听器,但我不确定这些东西是否是用于连续数据传输的。我真的很感激你给我一点建议。 编辑: 同样值得一提的是,我使用的是组件系统

  • 问题内容: 假设我有以下代码: 这段代码的问题在于,协程内部的循环永远不会完成第一次迭代,而大小会不断增加。 为什么会这样发生,我该怎么解决? 我无法摆脱单独的线程,因为在我的真实代码中,我使用了单独的线程与串行设备进行通信,而且我还没有找到使用的方法。 问题答案: 不是线程安全的,因此您不能直接在多个线程中直接使用它。相反,您可以使用,它是提供线程感知队列的第三方库: 还有(全披露:我写了它),

  • 问题内容: 我正在编写小型且非常干燥的框架,该框架高度依赖元数据。我想知道是否有一种方法来获取方法参数名称,即给定一些方法 得到的字符串和。 我知道我可以注释参数,但是那不是很好。 问题答案: 我们为包含参数名称的String[]的方法创建了一个自定义注释。与必须注释每个单独的参数相比,此方法感觉易于管理。我们计划添加构建时检查,以确保带注释的参数名称的数量与参数的数量匹配,因为这是我们所需要的。

  • 我是从https://wallpapersden.com/street-fighter-fortnite-wallpaper/下载壁纸的。当我点击下载按钮时,另一个页面打开了“https://images.wallpapersden.com/image/download/street-fighter-fortnite_bgtoaw6umzqarawkpjrmbmdlrwzlbwu.jpg”,我想知

  • 我正在使用Jersey创建一个RESTful API,其中实现了一个ExceptionMapper类来捕获所有错误。 有没有办法从异常中获取查询字符串?或者实现PreparedStatement并在其中创建一个方法来获取查询字符串?