Java 线程安全发布程序
我遇到了一个Java并发问题,我以为我已经解决了,但现在我觉得我的解决方案有问题 问题是: 以线程安全和高性能的方式实现缺少的方法。系统应仅在每个Java 线程安全发布程序,java,multithreading,concurrency,Java,Multithreading,Concurrency,我遇到了一个Java并发问题,我以为我已经解决了,但现在我觉得我的解决方案有问题 问题是: 以线程安全和高性能的方式实现缺少的方法。系统应仅在每个T key的第一次请求时订阅,并且应在给定key的侦听器不再保留时取消订阅 interface Listener { void onData(); } abstract class Publisher<T> { public void subscribe(T key, Listener l) {
T key
的第一次请求时订阅,并且应在给定key的侦听器不再保留时取消订阅
interface Listener {
void onData();
}
abstract class Publisher<T> {
public void subscribe(T key, Listener l) {
// TODO complete
}
public void unsubscribe(T key, Listener l) {
// TODO complete
}
public void publish(T key) {
// TODO complete
}
public abstract void reallyLongSubscribeRequest(T key);
public abstract void reallyLongUnsubscribeRequest(T key);
}
我现在担心这不再是线程安全的,因为
putIfAbsent
保持映射的一致性,但是真正长的方法将被调用两次(如果它更改类状态,这是不好的)你有几个问题。您的列表不是线程安全的,并且您是正确的,您可以多次运行请求。ConcurrentHashMap是获得并行但线程安全的映射访问的好方法。但是,您需要实现某种“按键”同步,以确保(取消)订阅操作正确进行(更不用说列表更新)。您有两个问题:
- 您的
、订阅
和取消订阅
方法应该是发布
,以使它们具有线程安全性同步的
- 您应该只有一个线程来执行等待
队列的
调用。您向reallyllong…()
发布一条消息,告诉您要做一件事或另一件事,它确实做了。队列将确保它们一个接一个地发生队列
您的代码中也有一个bug。只有在映射中不存在密钥时,才执行
reallyllongsubscriberequest(…)
,但在删除最后一个侦听器时,不会从映射中删除密钥。这不会阻止线程,直到reallyllong…()
返回,从而降低性能吗?我是说,还有一个线程,等等,我将举一个小例子,reallyllong…()
方法将在一个线程上连续运行,与执行(取消)订阅操作的主线程分离。这里的关键是,reallyllong…()
方法按顺序发生,或者至少按每个键的顺序发生,这一点非常重要。不同键的操作可以并行运行,这意味着键A的操作不会阻碍键B的另一个操作。@clicky我还为答案添加了一个注释,描述了您遇到的错误。@clicky如果您选择单线程选项(因此所有reallyllong…
操作都是连续运行的)您可以继续使用执行器服务,只需使用执行器。newSingleThreadExecutor()。如果要尝试序列化每个键的操作,则只需要自定义解决方案。你可以看看怎么做。
abstract class Publisher<T> {
private final int POOL_SIZE = 5;
private final ConcurrentHashMap<T, List<Listener>> listeners = new ConcurrentHashMap<>();
private final ScheduledExecutorService stpe = Executors.newScheduledThreadPool(POOL_SIZE);
public void subscribe(T key, Listener l) {
if (listeners.containsKey(key)) {
listeners.get(key).add(l);
} else {
final T keyToAdd = key;
final List<Listener> list = new LinkedList<Listener>();
list.add(l);
Runnable r = new Runnable() {
public void run() {
reallyLongSubscribeRequest(keyToAdd);
listeners.putIfAbsent(keyToAdd, list);
}
};
stpe.execute(r);
}
}
public void unsubscribe(T key, Listener l) {
if (listeners.containsKey(key)) {
List<Listener> list = listeners.get(key);
list.remove(l);
if (list.size() == 0) {
final T keyToRemove = key;
Runnable r = new Runnable() {
public void run() {
reallyLongUnsubscribeRequest(keyToRemove);
}
};
stpe.execute(r);
}
}
}
public void publish(T key) {
if (listeners.containsKey(key)) {
final List<Listener> list = listeners.get(key);
for (Listener l : list) {
l.onData();
}
}
}
public abstract void reallyLongSubscribeRequest(T key);
public abstract void reallyLongUnsubscribeRequest(T key);
}