ACoder
2009-05-13 21:32
从代码来看containerUtil应当是一个单体模式,由他来进行Cache的管理,forum对象代表什么??是整个论坛的跟还是一个子栏目??每个人取得的不一样么??我感觉应当是一样的东西。

>>构造过程也许很费时,不需要每个并发线程都构造一下。

这句话没有理解,每个并发线程只是获取而已啊。

xmuzyu
2009-05-13 21:42
>>这句话没有理解,每个并发线程只是获取而已啊。

在应用中,我对于复杂的聚合根,采用builder模式,而对于不太复杂简单点的实体或者聚合根则采用Factory进行,无论是Builder还是Factory,他们构造对象都是首先从缓存去,如果有就返回,如果没有就根据数据库来构造。假如有两个并发的线程都要获取同一个MODEL,结果发现这个Model不在缓存,这样两个并发线程就都需要从数据库构造,并且最后完成的线程会覆盖先前线程在缓存中写入得值(用前辈您的方法就可以实现不会覆盖)当然了覆盖也未尝不可,如果这个时候获取的对象不需要全局的写操作就可以忽略这种情况,但是如果获取的对象要进行写操作就必须要保证所有并发线程获得同一个对象。所以我才采用了那个监视器对象。

xmuzyu
2009-05-14 16:22
不好意思,昨天发布的这个客户端代码是错误的,是我以前的工程的代码(郁闷,eclipse里面几十个工程自己把自己都搞晕了),以前的那个版本有错误。

所有的客户端如果发现缓存中没有存在对象,就通过MutexManager获得Metux,然后在此Metux上同步。不过MutexManager要求每一个Model对应一个,如果是全局model对应一个,竞争会非常激烈。不知道banq老师还有什么好的办法,我也改进一下。

以下是:Mutex代码

public class Mutex {

private volatile AtomicInteger usingCount ;

private volatile Serializable key ;

public Mutex(Serializable key) {

this.key = key;

}

public void addCount() {

usingCount.incrementAndGet();

}

public int getCount() {

return usingCount.get();

}

public void decrementCount() {

usingCount.decrementAndGet();

}

public Object getKey() {

return key;

}

}

以下是MutexManager代码:

public class MutexManagerImpl implements MutexManager {

private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(

true);

private final Lock write = reentrantReadWriteLock.writeLock();

private final Map<Serializable, Mutex> mutexs = new ConcurrentHashMap<Serializable, Mutex>();

@Override

public Mutex getMutex(Serializable key) {

Mutex mutex = mutexs.get(key);

if (mutex != null) {

mutex.addCount();

return mutex;

}

return createMutex(key);

}

@Override

public void release(Mutex mutex) {

mutex.decrementCount();

if (mutex.getCount() > 0) {

return;

}

mutexs.remove(mutex.getKey());

}

private Mutex createMutex(Serializable key) {

//此锁只有在MODEL不在缓存,并且在针对第一个获得特定Id的线程才会用。

write.lock();

try {

Mutex mutex = mutexs.get(key);

if (mutex == null) {

mutex = new Mutex(key);

mutexs.put(key, mutex);

}

mutex.addCount();

return mutex;

} finally {

write.lock();

}

}

}

[该贴被xmuzyu于2009-05-14 16:23修改过]

[该贴被xmuzyu于2009-05-14 16:33修改过]

ACoder
2009-05-14 21:16
》》在应用中,我对于复杂的聚合根,采用builder模式,而对于不太复杂简单点的实体或者聚合根则采用Factory进行,无论是Builder还是Factory,他们构造对象都是首先从缓存去,如果有就返回,如果没有就根据数据库来构造。假如有两个并发的线程都要获取同一个MODEL,结果发现这个Model不在缓存,这样两个并发线程就都需要从数据库构造,并且最后完成的线程会覆盖先前线程在缓存中写入得值(用前辈您的方法就可以实现不会覆盖)当然了覆盖也未尝不可,如果这个时候获取的对象不需要全局的写操作就可以忽略这种情况,但是如果获取的对象要进行写操作就必须要保证所有并发线程获得同一个对象。所以我才采用了那个监视器对象。

我觉得可以这样设计,设计一个Model构造者,专门负责Model的构造,为单独的线程(ModelMakerThread)。对象获取者从缓存中未获取对象,则唤醒ModelMakerThread线程,向其任务队列中提交一条命令,然后该线程sleep,ModelMakerThread先检测缓存中是否存在Model,如果不存在则从数据库中生成Model,然后添加到Cache,最后唤醒等待线程。

banq
2009-05-15 14:08
>不过MutexManager要求每一个Model对应一个,如果是全局model对应一个,竞争会非常激烈。

没有绝对的安全,安全事务和性能是一对矛盾,适度就可以了,就象数据库的ACID锁策略选择一样,最安全的排他锁是最慢的。

有些问题要根据需求具体情况来决定这个适度,比如论坛这个应用,就可以要求松些,如果你是关键业务,那么要求严些。

加锁是为了写,如果写竞争会非常激烈,那么按照DDD思想,就将锁的范围缩小,将那些会发生写竞争字段独立成一个对象,然后在这个对象内部用不变性约束,加锁加在这个对象内,这样,能避免无辜,提高精准率。

[该贴被banq于2009-05-15 14:09修改过]

猜你喜欢