public synchronized Stock getStock(StockInfo info, boolean force) {

    if (info == null) {
      Log.v(TAG, "getStock - info is null");
      return null;
    }

    Log.v(TAG, "getStock - stock_id:" + info.stock_id + ", force:" + force);

    // 先内存
    Stock stock = cache.get(info.getKey());

    // 再sql
    if (stock == null) {
      stock = new Stock(info);
      cache.put(info.getKey(), stock);
    }

    // 最后网络
    if (stock.outOfDate()) {
      cache.remove(info.getKey());
      stock.updateSQL(force); // sync NET and persit to SQL

      stock = new Stock(info); // reload from SQL
      cache.put(info.getKey(), stock);
    }

    return stock;
  }
  public void put(K key, V value) {
    RefCountedMemory mem = serialize(value);
    if (mem == null) return; // out of memory.  never mind.

    RefCountedMemory old = map.put(key, mem);
    if (old != null) old.unreference();
  }
  public boolean putIfAbsent(K key, V value) {
    RefCountedMemory mem = serialize(value);
    if (mem == null) return false; // out of memory.  never mind.

    RefCountedMemory old = map.putIfAbsent(key, mem);
    if (old != null)
      // the new value was not put, we've uselessly allocated some memory, free it
      mem.unreference();
    return old == null;
  }
 public V get(K key) {
   RefCountedMemory mem = map.get(key);
   if (mem == null) return null;
   if (!mem.reference()) return null;
   try {
     return deserialize(mem);
   } finally {
     mem.unreference();
   }
 }
  public boolean replace(K key, V oldToReplace, V value) {
    // if there is no old value in our map, we fail
    RefCountedMemory old = map.get(key);
    if (old == null) return false;

    // see if the old value matches the one we want to replace
    RefCountedMemory mem = serialize(value);
    if (mem == null) return false; // out of memory.  never mind.

    V oldValue;
    // reference old guy before de-serializing
    if (!old.reference()) return false; // we have already freed hence noop.
    try {
      oldValue = deserialize(old);
    } finally {
      old.unreference();
    }
    boolean success = oldValue.equals(oldToReplace) && map.replace(key, old, mem);

    if (success) old.unreference(); // so it will be eventually be cleaned
    else mem.unreference();
    return success;
  }
  @SuppressWarnings("rawtypes")
  @Override
  public GrailsClass getArtefactForFeature(Object featureId) {
    if (artefactInfo == null) {
      return null;
    }

    String uri;
    String pluginName = null;

    if (featureId instanceof Map) {
      Map featureIdMap = (Map) featureId;
      uri = (String) featureIdMap.get("uri");
      pluginName = (String) featureIdMap.get("pluginName");
    } else {
      uri = featureId.toString();
    }

    GrailsClass controllerClass = uriToControllerClassCache.get(uri);
    if (controllerClass == null) {
      final ApplicationContext mainContext = grailsApplication.getMainContext();
      GrailsPluginManager grailsPluginManager = null;
      if (mainContext.containsBean(GrailsPluginManager.BEAN_NAME)) {
        final Object pluginManagerBean = mainContext.getBean(GrailsPluginManager.BEAN_NAME);
        if (pluginManagerBean instanceof GrailsPluginManager) {
          grailsPluginManager = (GrailsPluginManager) pluginManagerBean;
        }
      }
      final GrailsClass[] controllerClasses = artefactInfo.getGrailsClasses();
      // iterate in reverse in order to pick up application classes first
      for (int i = (controllerClasses.length - 1); i >= 0; i--) {
        GrailsClass c = controllerClasses[i];
        if (((GrailsControllerClass) c).mapsToURI(uri)) {
          boolean foundController = false;
          if (pluginName != null && grailsPluginManager != null) {
            final GrailsPlugin pluginForClass = grailsPluginManager.getPluginForClass(c.getClazz());
            if (pluginForClass != null && pluginName.equals(pluginForClass.getName())) {
              foundController = true;
            }
          } else {
            foundController = true;
          }
          if (foundController) {
            controllerClass = c;
            break;
          }
        }
      }
      if (controllerClass == null) {
        controllerClass = NO_CONTROLLER;
      }

      // don't cache for dev environment
      if (Environment.getCurrent() != Environment.DEVELOPMENT) {
        uriToControllerClassCache.put(uri, controllerClass);
      }
    }

    if (controllerClass == NO_CONTROLLER) {
      controllerClass = null;
    }
    return controllerClass;
  }
 public void put(K key, V value) {
   map.put(key, value);
 }
 public void clear() {
   map.clear();
 }
 public long weightedSize() {
   return map.weightedSize();
 }
 public int size() {
   return map.size();
 }
 public Set<K> getKeySet() {
   return map.keySet();
 }
 public boolean containsKey(K key) {
   return map.containsKey(key);
 }
 public Set<K> keySet() {
   return map.keySet();
 }
 public int getSize() {
   return map.size();
 }
 public void updateCapacity(int capacity) {
   map.setCapacity(capacity);
 }
 public int getCapacity() {
   return map.capacity();
 }
 public void remove(K key) {
   map.remove(key);
 }
 public V getInternal(K key) {
   return map.get(key);
 }
 public V get(K key) {
   V v = map.get(key);
   requests.incrementAndGet();
   if (v != null) hits.incrementAndGet();
   return v;
 }
 public void remove(K key) {
   RefCountedMemory mem = map.remove(key);
   if (mem != null) mem.unreference();
 }
 public long capacity() {
   return map.capacity();
 }
 public Set<K> hotKeySet(int n) {
   return map.descendingKeySetWithLimit(n);
 }
 public void setCapacity(long capacity) {
   map.setCapacity(capacity);
 }
 public boolean isEmpty() {
   return map.isEmpty();
 }
 public void clear() {
   map.clear();
   requests.set(0);
   hits.set(0);
 }