2014-12-02 53 views
0

我創建了一個簡單的搜索引擎:併發管理在Java中

public String search(Map<String, Object> params) { 
    String result = /*search*/; 
    return result; 
} 

此方法執行搜索過程並返回結果(JSON),但有一個很大的問題...... 因爲搜索過程是昂貴的,如果一些用戶同時使用相同的關鍵字執行搜索過程,搜索過程將分別爲每個執行!

我有兩個選擇,以避免這種情況:

1​​:

private static final ConcurrentHashMap<Object, ConcurrentLinkedQueue<Locker<Object, String>>> CONCURRENT_SEARCHES = new ConcurrentHashMap<>(); 

public String search(Map<String, Object> params) { 
    Object key = params.get("keyword"); 
    assert key != null; 
    ConcurrentLinkedQueue<Locker<Object, String>> queue = CONCURRENT_SEARCHES.get(key); 
    if (queue != null) { 
     System.out.println("waiting"); 
     Locker<Object, String> locker = new Locker<>(key); 
     queue.add(locker); 
     locker.lock(); 
     String result = locker.getValue(); 
     return result == null ? "[]" : result; 
    } 
    System.out.println("new search"); 
    CONCURRENT_SEARCHES.put(key, (queue = new ConcurrentLinkedQueue<>())); 
    String result = /*search*/; 
    CONCURRENT_SEARCHES.remove(key); 
    Locker<Object, String> locker; 
    while ((locker = queue.poll()) != null) { 
     locker.setValue(result); 
     locker.unlock(); 
    } 
    return result; 
} 
:如你所知,這不會使用共享對象恰好解決這個問題...

2-

這工作真的很好,但又有一個小問題。

測試

public static void main(String[] args) { 
    String id = "1"; 
    ProcessSession session = ProcessManager.openSession(id); 
    //... 
    ExecutorService service = Executors.newCachedThreadPool(); 
    for (int i = 0; i < 10; i++) { 
     service.submit(() -> session.search("keyword")); 
    } 
} 

輸出

new search 
waiting 
waiting 
new search 
waiting 
waiting 
waiting 
waiting 
waiting 
waiting 

編輯:

public final class Locker<K, V> { 

    private final Object o = new Object(); 
    private final K key; 
    private V value; 

    public Locker(K key) { 
     this.key = key; 
    } 

    public K getKey() { 
     return key; 
    } 

    public V getValue() { 
     return value; 
    } 

    public void setValue(V value) { 
     this.value = value; 
    } 

    public void lock() { 
     synchronized (o) { 
      try { 
       o.wait(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public void lock(long timeout) { 
     synchronized (o) { 
      try { 
       o.wait(timeout); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public void unlock() { 
     synchronized (o) { 
      o.notify(); 
     } 
    } 

} 

這是怎麼回事錯在這裏?任何人都可以提出一個更好的解

謝謝。

+0

那麼,您緩存搜索,而不是搜索結果。因此,如果沒有正在進行的關鍵字搜索,即使之前進行了類似的搜索,也會啓動新的關鍵字。 – kiheru 2014-12-02 11:39:43

+0

搜索結果不固定,可能由於某些原因而稍後更改... – FaNaJ 2014-12-02 11:42:49

+0

那麼測試運行時會出現什麼問題?大多數搜索與以前的搜索結合在一起。這不是你想要做的嗎? – kiheru 2014-12-02 11:48:16

回答

0

謝謝,問題就迎刃而解了:

private static final Map<Object, List<Locker<Object, String>>> CONCURRENT_SEARCHES = new ConcurrentHashMap<>(); 


public String search(Map<String, Object> params) { 
    Object key = params.get("keyword"); 
    assert key != null; 
    Locker<Object, String> locker = new Locker<>(key); 
    if (isInProgress(key, locker)) { 
     locker.lock(); 
     String result = locker.getValue(); 
     return result == null ? "[]" : result; 
    } 
    String result = /*search*/; 
    finished(key, result); 
    return result; 
} 

private static synchronized boolean isInProgress(Object key, Locker<Object, String> locker) { 
    List<Locker<Object, String>> list = CONCURRENT_SEARCHES.get(key); 
    if (list != null) { 
     list.add(locker); 
     return true; 
    } 
    CONCURRENT_SEARCHES.put(key, Collections.synchronizedList(new ArrayList<>())); 
    return false; 
} 

private static synchronized void finished(Object key, String result) { 
    Optional.of(CONCURRENT_SEARCHES) 
      .map(searches -> searches.remove(key)) 
      .ifPresent(list -> { 
       list.stream().forEach(locker -> { 
        locker.setValue(result); 
        locker.unlock(); 
       }); 
       list.clear(); 
      }); 
} 
0

使用併發收集的東西只會在細粒度的級別上進行鎖定,所以你不會獲得太多好處。

我會設計搜索有點不同。這裏的想法應該是設計你的search方法,使得它的兩次調用相互排斥。我想到的一個問題就是在這裏使用生產型消費模式。

  • Search方法爲每次調用創建一個worker,並且該worker被無限執行器服務執行。
  • 另一種方法是保持工人隊列。 search方法創建工作人員並將其添加到隊列中,另一個執行人員服務繼續選擇工作人員表單隊列以執行並返回結果。

在所有情況下,這個想法是讓搜索操作的實例互相排斥。如果可以的話,也緩存這些東西。

0

除了使用鎖和同步塊之外,還可以使用標誌來獲得更好的性能。

發生第一次搜索時,在搜索關鍵字爲關鍵字的地圖中設置一個標記「進行中」。 因此,當重複搜索時,您可以查找此地圖,並決定等待或進行長時間輪詢以從第一次搜索中獲取結果。

一旦搜索完成,緩存結果如果可能並刪除標誌。

您可以使用套接字編程,使用高級消息傳遞將結果推送給具有相同搜索關鍵字的用戶。這將節省資源並幫助實現更好的性能。