结构如何将事件正确通知其他线程?

How Can Structure Notify Other Threads Correctly About An Event?

我正在尝试编写一个线程安全结构来保留键值对并在一定时间延迟后自动删除它们。问题是,容器应该通知其他线程删除。

我也试过在同步块中使用 notifyAll(),但问题仍然存在。

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;


class Container <K extends Comparable<K>, V> {
    private ConcurrentHashMap <K, V> a;
    private final int delay = 2;
    private final Lock lock = new ReentrantLock();
    private final Condition removed = lock.newCondition();

    public Container() {
        a = new ConcurrentHashMap<>();
    }
    public synchronized void put(K k, V o) {
            lock.lock();
            a.put(k, o);
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(delay);


                    a.remove(k, o);
                    removed.signalAll();
                    lock.unlock();
                } catch (InterruptedException e){
                    e.printStackTrace();
                }

            }).start();

    }

    public  V get(int k)  {

        return a.get(k);
    }

    @Override
    public String toString() {
        return Stream.of(a)
        .filter(Objects::nonNull)
                .map(Object::toString)
                .collect(Collectors.joining(", ", "{", "}"));

    }
}

public class Main {

    public static void main(String [] args) throws InterruptedException {
        Container<Integer, Integer> c = new Container<>();
        c.put(0, 10);
        System.out.println(c);
        c.put(1, 11);
        c.put(2, 12);

        TimeUnit.SECONDS.sleep(3);
        System.out.println(c);

    }
}

程序以代码 0 结束并打印预期值:第一个元素和空结构。但无论哪种方式,我都得到了 IllegalMonitorStateException.
任何想法,谢谢。

public synchronized void put(K k, V o) {
    a.put(k, o);
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(delay);
            lock.lock();
            a.remove(k, o);
            removed.signalAll();
            lock.unlock();
        } catch (InterruptedException e){
            e.printStackTrace();
        }

    }).start();

}

也许试试这个。您在不同的线程中重新锁定锁和解锁锁。调用 put 方法的线程是使用 lock.lock() 的线程,因此它是唯一可以解锁它的线程。 new Thread(....) 中的代码属于另一个线程,因此调用 lock.unlock() 将不起作用。同样在当前代码中,我没有看到任何锁和条件的使用,你可以删除所有这些(除非你计划在我们看不到的其他地方使用它),你可以从并发映射中放置和删除担心自己管理访问权限。

为了 waitnotifyawaitsignal 如果使用 Condition)执行操作的线程必须拥有锁定它正在尝试等待或通知。

从你的例子来看:你在主线程中加了一个锁 (lock.put()),然后启动了一个新线程。新线程不拥有锁(它仍然由主线程持有),但尽管如此您尝试调用 signalAll() 并获取 IllegalMonitorStateException.

为了解决问题,您应该:
1. 主线程准备就绪时释放锁(此时你永远不会释放它)。
2.先锁定新线程,然后才调用signalAll()
3. 准备好后释放新线程中的锁。在 try-catch 块的 finally 子句中执行此操作,以保证在出现异常时释放锁。

还有几分钟:
- 为了得到通知,线程 必须等待 通知。
- put() 方法上的同步是多余的,因为您已经在内部使用了 ReentrantLock

这里有几件事要看:

  1. lock
  2. removed 这是使用 lock
  3. 创建的条件

您的主线程在 put 方法中获取 lock 上的锁。 但是它永远不会释放锁;相反,它会创建一个调用 removed.signalAll() 的新 Thread。但是,这个新线程不持有执行此操作所需的锁 lock

我认为你需要做的是确保每个锁定 lock 的线程也解锁它,并且每个调用 signalAll 的线程也有一个锁。

  public synchronized void put(K k, V o) {

    a.put(k, o);

    new Thread(() -> {
      try {
        TimeUnit.SECONDS.sleep(delay);

        lock.lock(); // get control over lock here
        a.remove(k, o);
        removed.signalAll();
        lock.unlock();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

    }).start();

  }