簡體   English   中英

更新/寫入靜態變量的最佳實踐?

[英]Best practice for updating/writing to static variable?

我有一個顯示部門文檔的項目。 我將所有文檔(從數據庫中獲取)存儲在靜態arrayList中。 每隔X小時,我就根據數據庫中的新doc(如果有的話)重建了arrayList。 還有一個靜態變量來控制重建該數組,在執行重建任務的方法中設置和取消設置。 每個訪問服務器的Web瀏覽器都將創建此類的實例,但doc arrayList和該控件變量在所有類實例之間共享。

Find-Bugs工具抱怨“從實例方法someClassMethod寫入靜態字段someArrayName和someVariableName”。 似乎這不是好事(讓類實例方法寫入靜態字段)。 有沒有人有好的建議如何解決這個問題? 謝謝。

根據FindBugs錯誤描述

ST:從實例方法寫入靜態字段(ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD)

此實例方法寫入靜態字段。 如果操作多個實例並且通常是不好的做法,這很難得到糾正。

除了並發問題之外,它意味着JVM中的所有實例都在訪問相同的數據,並且不允許兩個單獨的實例組。 如果您有一個單獨的“manager”對象並將其作為構造函數參數或至少作為setManager()方法參數傳遞給每個實例,那會更好。

至於並發問題:如果你必須使用靜態字段,你的靜態字段應該是最終的; 顯式同步很難。 (如果你正在初始化非最終靜態字段,還有一些棘手的方面,除了我對Java的了解,但我認為我已經在Java Puzzlers書中看到過它們。)至少有三種方法可以解決這個問題(警告,未經測試的代碼如下,在使用前先檢查):

  1. 使用線程安全的集合 ,例如,包含未以任何其他方式訪問的列表的Collections.synchronizedList

     static final List<Item> items = createThreadSafeCollection(); static List<Item> createThreadSafeCollection() { return Collections.synchronizedList(new ArrayList()); } 

    然后當你從一個實例替換這個集合時:

     List<Item> newItems = getNewListFromSomewhere(); items.clear(); items.add(newItems); 

    這個問題是,如果兩個實例同時執行此序列,您可以得到:

    Instance1:items.clear(); Instance2:items.clear(); Instance1:items.addAll(newItems); Instance2:items.addAll(newItems);

    並獲得一個不符合所需類不變量的列表,即在靜態列表中有兩組newItems。 因此,如果要將整個列表清除為一步,並將第二步添加為項,則此方法不起作用。 (但是,如果您的實例只需要添加一個項目, items.add(newItem)可以安全地從每個實例中使用。)

  2. 同步對集合的訪問。

    你需要一個明確的同步機制。 同步方法不起作用,因為它們在“this”上同步,這在實例之間不常見。 你可以使用:

     static final private Object lock = new Object(); static volatile private List<Item> list; // technically "list" doesn't need to be final if you // make sure you synchronize properly around unit operations. static void setList(List<Item> newList) { synchronized(lock) { list = newList; } } 
  3. 使用AtomicReference

     static final private AtomicReference<List<Item>> list; static void setList(List<Item> newList) { list.set(newList); } 

如果我理解您從Find Bugs正確發布的消息,這只是一個警告。

如果要隱藏警告,請從靜態方法執行修改。 查找錯誤警告您,因為這通常是一個錯誤。 程序員認為他們正在改變一些實例狀態,但實際上他們正在改變一些影響每個實例的狀態。

使用Singleton設計模式是一種方式。 您只能擁有一個包含所需值的對象實例,並通過全局屬性訪問該實例。 優點是,如果您希望以后有更多實例,則對預先存在的代碼的修改較少(因為您沒有將靜態字段更改為實例字段)。

您不需要每次都刪除列表。 如上所述,您將不得不處理多個線程,但您可以創建一次ArrayList,然后使用clear()和addAll()方法擦除和重新填充。 FindBugs應該對此非常滿意,因為你沒有設置靜態。

伙計們 - 如果這項技術有任何問題,請隨時填寫:-)

第二個想法是通過hibernate從數據庫中驅動東西。 所以不要維護一個列表,hibernate有內置的緩存,所以它幾乎一樣快。 如果你在數據庫級別更新數據(這意味着hibernate不知道),你可以告訴hibernate清除它的緩存並在下次查詢時從數據庫中刷新。

你不想這樣做。 每個請求都在自己的線程中運行。 如果在瀏覽器操作上執行的代碼修改了列表,則兩個請求可能同時修改列表,並破壞數據。 這就是為什么從非靜態上下文訪問靜態資源不是一個好主意,也可能是您的工具警告您的原因。

看這個

http://download.oracle.com/javase/6/docs/api/index.html?java/util/concurrent/package-summary.html

特別是關於ArrayList如何不同步的部分。 另請注意,我提到的段落有一個解決方案,具體而言

List list = Collections.synchronizedList(new ArrayList(...));

這是一種方法。 但它仍然不是一個好主意,因為它可能很慢。 如果它不是商業級的應用程序,並且你沒有大批量交易,你可能會因為沒有做得更好而得到。 如果這是每天只被點擊幾次的應用程序類型,您可以忽略該警告,並了解如果兩個請求互相攻擊, 可能會發生錯誤。

一個更好的解決方案:由於您擁有數據庫,我只需要根據需要從數據庫中獲取信息,即在請求進入時。您可以使用一些緩存技術來提高性能。

我不喜歡Singleton Pattern想法的原因是,即使它使警告消失,它本身也不能解決基本的同步問題。 然而,有線程安全的http://en.wikipedia.org/wiki/Singleton_pattern#Traditional_simple_way_using_synchronization ,這可能適用於這種情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM