[英]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. 为了
wait
或notify
( await
并signal
如果使用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: 这里有几件事要看:
lock
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.