Use volatile implement optimistic thread synchronization control

Recent projects in progress concurrent test of time found a problem, locate the piece of code like this:

//简化代码如下
private Map<String, Device> deviceMap = new ConcurrentHashMap<>();
/** 取缓存 */
public Device get(String key){
    return deviceMap.get(key);
}
/** 数据发生变化时会更新缓存 */
public void cacheDevices(){
    ...
    deviceMap.clear();
    deviceMap.putAll(...);
    ...
}

The core logic is very simple to read and write a simple local cache. Here's a thread is responsible for updating the cache, multiple threads will fetch cache. It is known that every time the cache can update the cache all data may be taken to a collection of the above, but in tens of thousands of concurrent access took place fail to target cache phenomenon. Not difficult to find where the problems found in the code, because there is no thread synchronization to take measures to clear cache set in the call and have not completed a new load of data when taking calls at this time caching method will fail, because at this time cache set is empty.

It is easy to think of using syncronized keywords to violently resolve any thread synchronization issues:

private Map<String, Device> deviceMap = new ConcurrentHashMap<>();
/** 取缓存 */
public Device get(String key){
    syncronized(deviceMap) {
        return deviceMap.get(key);
    }
}
/** 数据发生变化时会更新缓存 */
public void cacheDevices(){
    ...
    syncronized(deviceMap) {
        deviceMap.clear();
        deviceMap.putAll(...);
    }
    ...
}

However, in the case of highly concurrent read cache multi-threaded, this thread synchronization methods on the performance too. syncronized is "pessimistic", and it is assumed that the update is likely to conflict, each must first acquire a lock to go to the object you want to synchronize the operation, other threads to enter the synchronization time block will be blocked, enter the lock queue. In fact, in this scenario read much more than write, we should be "optimistic" a little, AtomicInteger atomic types such it provides a good model for us:

//JDK1.7 AtomicInteger源码
//volatile保证真实值的内存可见性
private volatile int value;
public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

This is a method of growing self integer atom. Here atoms update logic variable is non-blocking, is optimistic, it is assumed that relatively small update conflicts, the conflict processing using the retry loop, this synchronization method does not block other threads, in the case of updating a small performance loss less we can use this thought reform the code above:

private Map<String, Device> deviceMap = new ConcurrentHashMap<>();
private volatile boolean updatingCache = false;
/** 取缓存 */
public Device get(String key){
    while(true){
        if(!updatingCache){
            return deviceMap.get(key);
        }
    }
}
/** 数据发生变化时会更新缓存 */
public void cacheDevices(){
    ...
    updatingCache = true;
    deviceMap.clear();
    deviceMap.putAll(...);
    updatingCache = false;
    ...
}

Here increase updatingCache variable is used to tell whether the current set of threads being updated. In order to ensure modify updatingCache variables immediately visible to other threads, where the use of volatile modified, volatile variables will be written to main memory (relative to the thread private working memory) as soon as the update, other threads in the use of the variable must also be start the main memory value is updated, thereby ensuring visibility of memory variables. Here we must note that only just under a valid case to do so in the update thread, because volatile can only guarantee visibility, can not guarantee atomic operations, if there are multiple threads modify the volatile variables to ensure still have to operate by atomic lock sex.

If the update time-consuming operation, you can also transform the look, so read the thread properly so that the CPU time slice:

/** 取缓存 */
public Device get(String key){
    while(true){
        if(!updatingCache){
            return deviceMap.get(key);
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            ...
        }
    }
}

The above method is just an idea use a simple Java knowledge to solve problems quickly for the fact that the current code, under circumstances still sufficient time should be a good read about Guava cache such as cache mainstream class library source code, if necessary, direct introduction of sophisticated caching products .

Guess you like

Origin www.cnblogs.com/qingkongxing/p/11830545.html