ConcurrentHashMap支持的队列的线程安全 import java.util.Map; 导入java.util.concurrent.ConcurrentHashMap; 导入java.util.concurrent.Future; 导入org.slf4j.Logger; 导入org.slf4j.LoggerFactory; 公共类DedupingQueue实现QueueWrapper{ 私有静态最终记录器Logger=LoggerFactory.getLogger(duplicatingqueue.class); 私有最终映射项beingworkedon=新的ConcurrentHashMap(); private final AsyncWorker AsyncWorker;//包含线程池支持的异步方法 公共重复数据队列(AsyncWorker AsyncWorker){ this.asyncWorker=asyncWorker; } @凌驾 公共未来提交(E){ 如果(!itemsBeingWorkedOn.containsKey(e)){ itemsBeingWorkedOn.put(e,asyncWorker.executeWorkAsync(e,this)); }否则{ debug(“拒绝[{}],因为它已经在处理中”,e); } 返回正在处理的项目get(e); } @凌驾 公开作废完成(E){ debug(“Completed[{}]”,e); 正在处理的项目移除(e); } @凌驾 公共空间拒绝和审判(E){ itemsBeingWorkedOn.putIfAbsent(e,asyncWorker.executeWorkAsync(e,this)); } }
我很难推理上述代码的线程安全性 我认为ConcurrentHashMap支持的队列的线程安全 import java.util.Map; 导入java.util.concurrent.ConcurrentHashMap; 导入java.util.concurrent.Future; 导入org.slf4j.Logger; 导入org.slf4j.LoggerFactory; 公共类DedupingQueue实现QueueWrapper{ 私有静态最终记录器Logger=LoggerFactory.getLogger(duplicatingqueue.class); 私有最终映射项beingworkedon=新的ConcurrentHashMap(); private final AsyncWorker AsyncWorker;//包含线程池支持的异步方法 公共重复数据队列(AsyncWorker AsyncWorker){ this.asyncWorker=asyncWorker; } @凌驾 公共未来提交(E){ 如果(!itemsBeingWorkedOn.containsKey(e)){ itemsBeingWorkedOn.put(e,asyncWorker.executeWorkAsync(e,this)); }否则{ debug(“拒绝[{}],因为它已经在处理中”,e); } 返回正在处理的项目get(e); } @凌驾 公开作废完成(E){ debug(“Completed[{}]”,e); 正在处理的项目移除(e); } @凌驾 公共空间拒绝和审判(E){ itemsBeingWorkedOn.putIfAbsent(e,asyncWorker.executeWorkAsync(e,this)); } },java,multithreading,Java,Multithreading,我很难推理上述代码的线程安全性 我认为complete和rejectAndretry是完全线程安全的,因为map是线程安全的。但是如果AsyncWorker本身不是线程安全的,那么submit呢?另外,我如何才能以最有效的方式使其线程安全,而不使用synchronized(使用ConcurrentHashMap的内置保证)?submit()不是线程安全的,因为您的check-then-act序列不是原子的。一个线程的特定键的状态可以在检查之后和调用之前由另一个线程更改。使用ConcurrentH
complete
和rejectAndretry
是完全线程安全的,因为map是线程安全的。但是如果AsyncWorker
本身不是线程安全的,那么submit
呢?另外,我如何才能以最有效的方式使其线程安全,而不使用synchronized
(使用ConcurrentHashMap的内置保证)?submit()
不是线程安全的,因为您的check-then-act序列不是原子的。一个线程的特定键的状态可以在检查之后和调用之前由另一个线程更改。使用ConcurrentHashMap中的原子方法(很可能是computeIfAbsent()
)修复此问题
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DedupingQueue<E> implements QueueWrapper<E> {
private static final Logger LOGGER = LoggerFactory.getLogger(DedupingQueue.class);
private final Map<E, Future<E>> itemsBeingWorkedOn = new ConcurrentHashMap<>();
private final AsyncWorker<E> asyncWorker; //contains async method backed by a thread pool
public DedupingQueue(AsyncWorker<E> asyncWorker) {
this.asyncWorker = asyncWorker;
}
@Override
public Future<E> submit(E e) {
if (!itemsBeingWorkedOn.containsKey(e)) {
itemsBeingWorkedOn.put(e, asyncWorker.executeWorkAsync(e, this));
} else {
LOGGER.debug("Rejected [{}] as it's already being worked on", e);
}
return itemsBeingWorkedOn.get(e);
}
@Override
public void complete(E e) {
LOGGER.debug("Completed [{}]", e);
itemsBeingWorkedOn.remove(e);
}
@Override
public void rejectAndRetry(E e) {
itemsBeingWorkedOn.putIfAbsent(e, asyncWorker.executeWorkAsync(e, this));
}
}
如果AsyncWorker
不是线程安全的,那么整个类也不是线程安全的。但是,如果没有AsyncWorker
源代码,就很难对其进行推理。submit()
不是线程安全的,因为您的check-then-act序列不是原子的。一个线程的特定键的状态可以在检查之后和调用之前由另一个线程更改。使用ConcurrentHashMap中的原子方法(很可能是computeIfAbsent()
)修复此问题
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DedupingQueue<E> implements QueueWrapper<E> {
private static final Logger LOGGER = LoggerFactory.getLogger(DedupingQueue.class);
private final Map<E, Future<E>> itemsBeingWorkedOn = new ConcurrentHashMap<>();
private final AsyncWorker<E> asyncWorker; //contains async method backed by a thread pool
public DedupingQueue(AsyncWorker<E> asyncWorker) {
this.asyncWorker = asyncWorker;
}
@Override
public Future<E> submit(E e) {
if (!itemsBeingWorkedOn.containsKey(e)) {
itemsBeingWorkedOn.put(e, asyncWorker.executeWorkAsync(e, this));
} else {
LOGGER.debug("Rejected [{}] as it's already being worked on", e);
}
return itemsBeingWorkedOn.get(e);
}
@Override
public void complete(E e) {
LOGGER.debug("Completed [{}]", e);
itemsBeingWorkedOn.remove(e);
}
@Override
public void rejectAndRetry(E e) {
itemsBeingWorkedOn.putIfAbsent(e, asyncWorker.executeWorkAsync(e, this));
}
}
如果AsyncWorker
不是线程安全的,那么整个类也不是线程安全的。但是,如果没有AsyncWorker
源代码,就很难对其进行推理。有compute()
方法,该函数获取键、当前值或null
(如果没有当前映射),并返回要保留在映射中的值,或者在返回null
时将其删除
if (!itemsBeingWorkedOn.containsKey(e)) {
itemsBeingWorkedOn.put(e, asyncWorker.executeWorkAsync(e, this));
}
@覆盖
公共未来提交(E){
returnitemsbeingworkedon.compute(e,(k,v)->{
如果(v==null){
返回asyncWorker.executeWorkAsync(k,this);
}否则{
debug(“拒绝[{}],因为它已经在处理中”,k);
返回v;
}
});
}
请注意,您的队列没有排序。如果您需要FIFO或后进先出顺序,您应该使用更合适的数据结构,例如带有同步语句的
LinkedHashMap
。有compute()
方法,该方法使用键、当前值或null
,如果没有当前映射,并返回要保留在映射中的值,如果返回null
,则将其删除
if (!itemsBeingWorkedOn.containsKey(e)) {
itemsBeingWorkedOn.put(e, asyncWorker.executeWorkAsync(e, this));
}
@覆盖
公共未来提交(E){
returnitemsbeingworkedon.compute(e,(k,v)->{
如果(v==null){
返回asyncWorker.executeWorkAsync(k,this);
}否则{
debug(“拒绝[{}],因为它已经在处理中”,k);
返回v;
}
});
}
请注意,您的队列没有排序。如果您需要FIFO或LIFO顺序,您应该使用更合适的数据结构,例如
LinkedHashMap
和synchronized
语句。线程安全性取决于您期望的行为。如果覆盖现有密钥和/或不返回相同的值是可以的,那么submit()
是线程安全的。线程安全取决于您期望的行为。如果覆盖现有密钥和/或不返回相同的值是可以的,则submit()
是线程安全的。部分学分。关于序列不是原子的,您是正确的,但是putIfAbsent
不合适,因为它仍然会始终执行asyncWorker.executeWorkAsync(e,this)
,但不一定要将它放在映射中。因此需要compute()
或computeIfAbsent()
。AsyncWorker
的线程安全性不会影响整个类,特别是complete
和rejectAndRetry
(问题中也指出了这一点)。谢谢,你说得对,putIfAbsent
在这里不起作用,应该使用computeifassent
。我有疑问,所以写了“原子方法”。将编辑我的答案。关于第二部分,我从未见过关于特定类方法的线程安全性的推理,从类逻辑中也可以清楚地看出,如果没有submit()
,其他方法就没有意义。PlusrejectAndRetry()
还使用了AsyncWorker
。由于remove()
是线程安全的,因此complete()
在逻辑上是线程安全的。不过,您可能会得到一条重复的日志记录行,因此严格来说,它不是线程安全的。仔细观察拒绝和重试
,我无法真正理解它的预期用途。另外,使用非线程安全的submit()
可以使用相同的密钥完成并重试另一个线程任务,而不是原来的任务。是的,submit()
不是线程安全的。那是确定的,部分信用。你