在线程安全对象列表中同步多个读取和写入

时间:2015-06-12 07:13:40

标签: java multithreading concurrency synchronization thread-safety

假设我有一个线程安全类容器:

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循环,因为我还需要保持并发读取

在给定这些约束的情况下,您将如何处理所描述的场景?

1 个答案:

答案 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,因此线程被阻止的唯一情况是线程尝试从空队列中检索项目时。