简体   繁体   English

结构如何正确通知事件相关的其他线程?

[英]How Can Structure Notify Other Threads Correctly About An Event?

I'm trying to write a threadsafe structure to keep key - value pairs and to automatically remove them after certain time delay. 我正在尝试编写一个线程安全的结构来保留键-值对,并在一定时间延迟后自动将其删除。 The problem is, that the container should notify other threads about deletion. 问题是,容器应通知其他线程有关删除的信息。

I've also tried to use notifyAll() in a synchronized block, but the problem persists. 我也尝试在同步块中使用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);

    }
}

Program finishes with code 0 and prints expected values: first element and the empty structure. 程序以代码0结尾,并打印期望值:第一个元素和空结构。 But in either way I got the IllegalMonitorStateException . 但是无论哪种方式,我都得到了IllegalMonitorStateException
Any thoughts, thanks. 有什么想法,谢谢。

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();

}

Try this maybe. 试试这个吧。 You re locking the lock and unlocking the lock in different threads. 您重新锁定该锁,并在不同线程中解锁该锁。 The thread that call theput method is the one using lock.lock() so it is the only one that can unlock it. 调用put方法的线程是使用lock.lock()的线程,因此它是唯一可以解锁它的线程。 The code inside the new Thread(....) belong to another thread so calling lock.unlock() there will not work. 新Thread(....)中的代码属于另一个线程,因此在这里调用lock.unlock()将不起作用。 Also in the current code I don't see any use of the lock and condition, you can just remove all this (unless you plan on using it somewhere else we don't see), you can put and remove from a concurrent map with worrying about managing the access yourself. 同样在当前代码中,我看不到对锁和条件的任何使用,您可以删除所有这些内容(除非您计划在我们未看到的其他地方使用它),可以使用以下命令从并发映射中删除担心自己管理访问。

In order to wait or notify ( await and signal if using Condition ) the thread, which performs an action, must own the lock it's trying to wait or notify on. 为了waitnotifyawaitsignal如果使用Condition ),执行操作的线程必须拥有它试图等待或通知的锁。

From your example: you put a lock in the main thread ( lock.put() ), then start a new thread. 从您的示例中:您在主线程中设置了一个锁( lock.put() ),然后启动了一个新线程。 The new thread does not own the lock (it's still held by the main thread), but despite that you try to invoke signalAll() and get IllegalMonitorStateException . 新线程不拥有该锁(它仍由主线程持有),但是尽管如此,您还是尝试调用signalAll()并获取IllegalMonitorStateException

In order to fix the problem you should: 为了解决该问题,您应该:
1. Release the lock the main thread holds when it's ready (you never release it at the moment). 1.准备好主线程时释放它的锁(此刻您永远不会释放它)。
2. Lock in the new thread first and only then call signalAll() 2.首先锁定新线程,然后再调用signalAll()
3. Release the lock in the new thread when you're ready. 3.准备就绪后,释放新线程中的锁。 Do it in the finally clause of the try-catch block to guarantee that the lock gets released in case of an exception. 在try-catch块的finally子句中执行此操作,以确保在发生异常的情况下释放锁定。

Also a few moments: 还有一会儿:
- in order to be notified a thread must be waiting for notification. -为了得到通知,线程必须等待通知。
- synchronization on the put() method is redundant, since you're already using a ReentrantLock inside. put()方法上的同步是多余的,因为您已经在其中使用了ReentrantLock

We have a few things to look at here: 这里有几件事要看:

  1. lock
  2. removed which is a condition created using lock removed这是使用lock创建的条件

Your main thread acquires a lock on lock in the put method. 您的主线程在put方法中获得了对lock However it never releases the lock; 但是,它永远不会释放锁。 instead it creates a new Thread that calls removed.signalAll() . 相反,它将创建一个新Thread ,该Thread调用removed.signalAll() However this new thread does not hold a lock lock which would be required to do this. 但是,此新线程不具有执行此操作所需的lock

I think what you need to do is to make sure that every thread that locks lock also unlocks it and that every thread calling signalAll also has a 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();

  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用wait()并正确通知Java中的线程? - How do I use wait() and notify correctly with threads in java? 线程和观察者模式。 我怎样才能让一个线程通知所有其他线程并执行某个方法/事物? - Threads and observer pattern. How can i make a thread notify all of the other threads and execute a certain method/thing? 如何使两个线程等待并相互通知 - How to make two threads wait and notify each other 如何使用等待/通知使这些线程交替工作? - How can I use wait/notify to make these threads work alternatively? 如何在不是线程的对象上调用 wait() 和 notify() 方法? - How can the wait() and notify() methods be called on Objects that are not threads? 如何正确实例化类中的公共类+数据结构,以便其他对象可以使用它? - How do I correctly instantiate a public class+data structure in a class so that other objects can use it? 如何从Java中的其他子类通知父类有关其对象修改的信息? - How to notify the parent class about its object modification from other child class in java? 2个线程如何相互通信? - How can 2 threads communicate each other? 如何通知其他团队成员重要的项目变更(使用SVN或Ant)? - How to notify other team members about important project changes (using SVN or Ant)? 3 Android Threads,如何构建 - 3 Android Threads, how to structure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM