假设我有一个线程安全类容器:
public class Container {
private List<MyObject> objects;
...
public synchronized MyObject take(Type t) {...} //takes an object of type t
public synchronized void put(MyObject o) {...} //put an object
}
重要说明:MyObject有一个方法getType(),它返回一个他的类型
某处,我有一个容器列表(共享线程):
List<Container> containers;
我有一些线程执行这样的事情:
//This piece of code is not synchronized
for(Container c : containers) {
Type t = getRandomType();
MyObject o = c.getObject(t); //note that this is synchronized
if(o != null) { //if I found an object of the desired type
//do some important stuff
return;
}
}
waitingReaders.putMyself(); //wait for the object of the right type
我想遍历每个容器以获取正确类型的对象。
然后我有其他线程做这样的事情:
//This piece of code is not synchronized
Container c = getRandomContainer(); //from containers
Type t = getRandomType();
MyObject o = new MyObject(t); //creates an object of a random type
if(waitingReaders.containSomeoneWaitingForThisType(t)) {
waitingReaders.givesObjectToHim(o);
return;
}
else
c.put(o); //note that this is synchronized
EDITED:如果读者找不到所需类型为t的对象,他会让自己在结构waitReaders中等待它(这与他的实现并不重要)。如果作者找到一个等待生成的对象的读者,那么他会将它交给等待的读者(而不是放入容器中)。
问题是,编写者线程可以将某个类型为t的对象放在某个容器之后读者线程已经在for循环中分析了该容器。因此,如果读者是正确的类型,那么读者就会错过该对象(它在循环中已经太过领先了)。
同时,我不想锁定for循环,因为我还需要保持并发读取。
在给定这些约束的情况下,您将如何处理所描述的场景?
答案 0 :(得分:1)
如果我理解正确,你需要一个解决方案,其中多个线程可以存储MyObject
个实例,多个线程可以检索特定类型的MyObject
个实例,所有非阻塞都是唯一的例外,当时没有MyObject
个请求类型的实例可用,检索线程可能会被阻塞,直到所请求类型的新实例可用。
请勿尝试自行实施存储解决方案。使用现有的并发工具:
final ConcurrentHashMap<Type, BlockingQueue<MyObject>> map=new ConcurrentHashMap<>();
/**
* Get a object of {@code Type}, blocking if necessary
*/
MyObject getObjectOf(Type t) throws InterruptedException {
return map.computeIfAbsent(t, x->new LinkedBlockingQueue<>()).take();
}
/**
* Store an object, never blocking.
*/
void putObject(MyObject o) {
map.computeIfAbsent(o.getType(), x->new LinkedBlockingQueue<>()).add(o);
}
这使用Java 8.如果您没有Java 8,则必须模拟computeIfAbsent
操作:
final ConcurrentHashMap<Type, BlockingQueue<MyObject>> map=new ConcurrentHashMap<>();
/**
* Get a object of {@code Type}, blocking if necessary
*/
MyObject getObjectOf(Type t) throws InterruptedException {
return getQueue(t).take();
}
/**
* Store an object, never blocking.
*/
void putObject(MyObject o) {
getQueue(o.getType()).add(o);
}
private BlockingQueue<MyObject> getQueue(Type key) {
BlockingQueue<MyObject> q=map.get(key);
if(q!=null) return q;
BlockingQueue<MyObject> newQueue=new LinkedBlockingQueue<>();
q=map.putIfAbsent(key, newQueue);
return q==null? newQueue: q;
}
每个LinkedBlockingQueue
使用一个无界Type
,因此线程被阻止的唯一情况是线程尝试从空队列中检索项目时。