【问题标题】:Java singleton pattern - updates to instantiated variableJava 单例模式 - 更新实例化变量
【发布时间】:2015-09-26 15:55:49
【问题描述】:

想弄清楚单例模式。

如果我实现如下所示的单例模式,我该怎么做才能让其他类更新并查看字段 someString 和 someInt 的更新?

从我读到的关于单例模式的内容来看,不变性不是先决条件之一。那么从技术上讲,我可以为字段设置方法并更改它们并使这些更改对其他类可见吗?例如,如果我有另外两个类实现 Runnable 并每隔几秒打印 Foo 的字段。我试过了,结果是每个类只看到自己的更新,而其他类都看不到。

public class Foo {
    private static Foo instance;
    private String someString;
    private int someNum;

    private Foo(){
        someString = "a";
        someNum = 1;
    }

    public static Foo getInstance(){
        if(instance == null){
            instance = new Foo();
        }
        return instance;
    }

    public void setSomeString(String someString) {
        this.someString = someString;
    }

    public void setSomeNum(int someNum) {
        this.someNum = someNum;
    }

    @Override
    public String toString() {
        return "Foo{" +
                "someString='" + someString + '\'' +
                ", someNum=" + someNum +
                '}';
    }
}

---更新--- 添加了 2 个类(下面的 Baz 和 Bar)并使用 setter 更新了 Foo 并覆盖了 toString()。

首先运行 Baz,我希望它每秒打印 foo.toString() 的最新值。

然后运行 ​​Bar,它首先更新 Foo 的字段,然后每秒打印 foo.toString()。来自 Bar 的更新仅对 Bar 可见,而对 Baz 不可见。

来自 Baz 的输出:

1443284013576 Foo{someString='a', someNum=1}

1443284014576 Foo{someString='a', someNum=1}

1443284015576 Foo{someString='a', someNum=1}

1443284016577 Foo{someString='a', someNum=1}

1443284017577 Foo{someString='a', someNum=1}

1443284018577 Foo{someString='a', someNum=1}

Bar 的输出:

1443284016416 Foo{someString='abc', someNum=2}

1443284017417 Foo{someString='abc', someNum=2}

1443284018417 Foo{someString='abc', someNum=2}

1443284019418 Foo{someString='abc', someNum=2}

1443284020418 Foo{someString='abc', someNum=2}

public class Baz {
    public static void main(String[] args) throws InterruptedException {
        Foo foo = Foo.getInstance();
        while(true){
            System.out.println(foo);
            Thread.sleep(1000);
        }
    }
}


public class Bar{
    public static void main(String[] args) throws InterruptedException {
        Foo foo = Foo.getInstance();
        foo.setSomeNum(2);
        foo.setSomeString("abc");
        while(true){
            System.out.println(foo);
            Thread.sleep(1000);
        }
    }
}

更新:一些愚蠢的错别字

【问题讨论】:

  • 仅供参考,为了线程安全,您应该考虑将 getInstance() 设为同步方法。
  • 你能用你试过的设置器发布这个例子吗?
  • 当你像public static synchronized Foo getInstance()这样声明getInstance方法时会发生什么?
  • @BYTERIDER 见上文
  • @JamesKPolk 如果我的问题与同步有关,我不希望看到更新或获得 ConcurrentModificationException 吗?

标签: java design-patterns singleton


【解决方案1】:

您有两个单独的主要方法,因此您可能在单独的 JVM 中运行每个类。而是创建一个 main 方法,在不同的线程中运行每个类,然后运行它。

您还需要将Foo 中的方法声明为同步或等效的东西,以保证所有线程都能看到更新。

public class Foo {
    private static Foo instance;
    private String someString;
    private int someNum;

    private Foo() {
        someString = "a";
        someNum = 1;
    }

    public synchronized static Foo getInstance(){
        if(instance == null) {
            instance = new Foo();
        }
        return instance;
    }

    public synchronized void setSomeString(String someString) {
        this.someString = someString;
    }

    public synchronized void setSomeNum(int someNum) {
        this.someNum = someNum;
    }

    @Override
    public synchronized String toString() {
        return "Foo{" +
                "someString='" + someString + '\'' +
                ", someNum=" + someNum +
                '}';
    }
}

public class Baz implements Runnable {
    public void run() {
        Foo foo = Foo.getInstance();
        while(true) {
            System.out.println("Baz: " + foo);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class Bar implements Runnable {
    public void run() {
        Foo foo = Foo.getInstance();
        foo.setSomeNum(2);
        foo.setSomeString("abc");
        while(true) {
            System.out.println("Foo: " + foo);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Bar()).start();
        new Thread(new Baz()).start();
    }
}

【讨论】:

    【解决方案2】:

    Java 要求程序员在多线程程序中显式包含一些synchronizing 访问共享资源的机制。 Java为此提供了许多特性,但初学者可能应该从类的所有相关方法上的同步关键字开始。在您的示例中,通过不同步 getInstance() 方法,您可能会生成多个类的实例。如果未能同步您的其他方法,您将面临不确定行为的风险。

    要获得同步访问的好处,您只需将 synchronized 关键字添加到您的方法声明中,例如 public static synchronized Foo getInstance(){public synchronized void setSomeString(String someString) {

    【解决方案3】:

    是的,您可以使用 setter 和 getter。要从另一个类访问 setter,您可以使用 Foo.getInstance().setSomeString(someString)。

    【讨论】:

    • 查看我的更新。上述类的问题仍然存在。
    【解决方案4】:

    从您的单例类中,如果创建了对象,则不允许任何对象因为单例模式而修改 someNum 和 someString 值。在多线程应用程序中可能有机会打破单例模式。这将导致您的值不可靠。

    【讨论】:

    • 查看我的更新。我不想要不变性。只是一种将单个变量的更新发布到其他类的方法。
    猜你喜欢
    • 2010-10-01
    • 2014-10-05
    • 1970-01-01
    • 2014-09-14
    • 2011-06-10
    • 1970-01-01
    • 2021-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多