簡體   English   中英

線程安全的Enum Singleton

[英]Thread-safe Enum Singleton

枚舉有利於創建單身人士。 我知道枚舉方法不是線程安全的,所以我試着讓它成為線程安全的。 任何人都可以確認這種實施是否正確。 是否可以使用靜態和易失性這么多地方並且可以進行優化? 由於內部類是私有的,因此我必須在枚舉中創建函數以訪問內部類功能。 可以優化嗎?

import java.util.Date;

public enum SingletonWithEnum {
    INSTANCE;   

    private static class Singleton{

        private static volatile int count;
        private static volatile Date date;      

        public static  int getCount() { return count;}

        public static void setCount(int countParam) { synchronized(Singleton.class){ count = countParam; }}

        public static Date getDate() {  return date;}

        public static void setDate(Date dateParam) { synchronized(Singleton.class){ date = dateParam;}}

        public static String printObject() {
            return "Singleton [count=" + getCount() + ", date=" + getDate() + "]";
        }

    }

    public int getCount() { return Singleton.getCount();}

    public void setCount(int countParam)    {Singleton.setCount(countParam);}

    public Date getDate() { return Singleton.getDate();}

    public void setDate(Date dateParam) {Singleton.setDate(dateParam);}

    public String toString(){return Singleton.printObject();}
};

我這樣用它。

SingletonWithEnum object1 = SingletonWithEnum.INSTANCE;
object1.setCount(5);
object1.setDate(new Date());

首先,你的枚舉中不需要嵌套類。 你只需要在枚舉本身中定義成員和方法,即

enum Blah {
  INSTANCE;
  private int someField;
  public int getSomeField() { return someField; }
}

現在,您可以通過以下方式訪問單例方法:

int someField = Blah.INSTANCE.getSomeField();

此外,使成員靜態在這里是一種反模式,因為單例實例應該擁有其成員。 所以它們應該是實例變量,而不是靜態變量。 事實上,只有一個單例實例可確保您的JVM中只有一個每個成員的實例。

就線程安全而言,我個人更喜歡原子變量而不是volatile,例如:

private final AtomicInteger count = new AtomicInteger();
private final AtomicReference<Date> date = new AtomicReference<>(new Date());

請注意,它們必須被聲明為final才能真正保證線程安全,因為原子變量本身不會改變,盡管它們的值可以。

如果您只需要編碼的操作,那么volatile變量應該可以工作。 原子變量提供了一些操作,而不是它們的易失性對應物,例如,針對Java 7的compareAndSet和針對Java 8的getAndUpdateupdateAndGet 。請參閱討論。

但是,如果您的成員變量是線程安全的並且它們的線程安全策略是獨立的,那么您聲明它們(原子/易失性),您不需要擔心單例中方法的安全性。 如果你需要例如一次性原子地更新兩個變量,那么你必須重新考慮設計並引入適當的鎖(在設置獲取它們的值時)。

非常謹慎地修改Date對象的方式非常重要。 Date 不是線程安全的,所以我強烈建議在進行更改時返回副本並用副本替換實例,即(假設您使用的是AtomicReference ),

public Date getDate() { return new Date(date.get().getTime()); }
public void setDate(Date d) {
  date.set(new Date(d.getTime()));
}

最后,我強烈推薦Brian Goetz的Concurrency in Practice和Joshua Bloch的Effective Java,分別了解有關並發和單例模式的更多信息。

暫無
暫無

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

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