同时从集合中查找和删除元素或等待

时间:2016-10-21 00:44:27

标签: java multithreading

public class MyClass {
    private List<Integer> resources = new ArrayList<>();

    public synchronized Integer getAndRemoveResourceOrWait(Integer requestedResource) throws InterruptedException {
        while(resources.stream().anyMatch((r) -> { return r >= requestedResource; })) {
            wait();
        }
        Integer found = resources.stream().findFirst((r) -> {
            return r >= requestedResource;
        }).get();
        resources.remove(found);
        return found;
    }

    public void addResource(Integer resource) {
        resources.add(resource);
        notifyAll();
    }
}

线程&#34; A&#34;不定期地使用随机值调用addResource。 一些其他线程主动调用getAndRemoveResourceOrWait。

让方法getAndRemoveResourceOrWait同时工作需要做些什么?

例如,线程&#34; X&#34;使用变量128调用getAndRemoveResourceOrWait,这在资源集合中不存在。所以,它等待它。在等待的时候,线程&#34; Y&#34;使用变量64调用getAndRemoveResourceOrWait,它存在于资源集合中。线程&#34; Y&#34;不应该等待线程&#34; X&#34;完成。

1 个答案:

答案 0 :(得分:1)

  

让方法getAndRemoveResourceOrWait同时工作需要做些什么?

它只需要在与调用addResource(resource)的其他线程上运行。

请注意,getAndRemoveResource是阻塞(同步)操作,因为调用的线程被阻塞,直到得到答案。但是,一个调用getAndRemoveResource的线程不会阻止另一个调用getAndRemoveResource的线程。关键是wait()调用释放互斥锁,然后在通知互斥锁时重新获取互斥锁。这里会发生的是notifyAll将导致所有等待的线程一次一个。

但是,您的addResource方法存在错误。该方法需要声明为synchronized。如果您在当前线程持有notifyAll()的互斥锁时没有调用this,则会出现异常。 (这也是确保共享resources对象的更新在两个方向都可见的必要条件。)

此外,这种实施方式不会很好地扩展:

  • 每个等待的线程将在每次更新时扫描整个资源列表;即每次拨打addResource
  • 当等待线程找到资源时,它会再次扫描列表两次以将其删除。
  • 所有这一切都是在共享MyClass实例上持有互斥锁的时候完成的......这也阻止了addResource

更新 - 假设Resource值是唯一的,更好的解决方案是将ArrayList替换为TreeSet。这应该有效:

public class MyClass {
    private TreetSet<Integer> resources = new TreeSet<>();

    public synchronized Integer getAndRemoveResourceOrWait(
            Integer resource) throws InterruptedException {
        while (true) {
            Integer found = resources.tailSet(resource, true).pollFirst();
            if (found != null) {
                return found;
            }
            wait();
        }
    }

    public synchronized void addResource(Integer resource) {
        resources.add(resource);
        notifyAll();
    }
}

(我也试过了ConcurrentSkipListSet但是我无法找到一种方法来避免在添加和删除时使用互斥锁。如果你试图删除相同的资源,可以做到...... )