加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 线程安全的Enum Singleton

发布时间:2020-12-15 04:25:15 所属栏目:Java 来源:网络整理
导读:枚举有利于创建单身人士.我知道枚举方法不是线程安全的,所以我试着让它成为线程安全的.任何人都可以确认这种实施是否正确.是否可以使用静态和易失性这么多地方并且可以进行优化?由于内部类是私有的,因此我必须在枚举中创建函数以访问内部类功能.可以优化吗
枚举有利于创建单身人士.我知道枚举方法不是线程安全的,所以我试着让它成为线程安全的.任何人都可以确认这种实施是否正确.是否可以使用静态和易失性这么多地方并且可以进行优化?由于内部类是私有的,因此我必须在枚举中创建函数以访问内部类功能.可以优化吗?

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的getAndUpdate和updateAndGet.有关讨论,请参阅this.

但是,如果您的成员变量是线程安全的并且它们的线程安全策略是独立的,那么您声明它们(原子/易失性),您不需要担心单例中方法的安全性.如果你需要例如一次性原子地更新两个变量,那么你必须重新考虑设计并引入适当的锁(在设置和获取它们的值时).

非常谨慎地修改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,分别了解有关并发和单例模式的更多信息.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读