Java 单例模式 - 更新实例化变量

Java singleton pattern - updates to instantiated variable

想弄清楚单例模式。

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

根据我对单例模式的了解,不变性不是先决条件之一。所以从技术上讲,我可以为字段设置 setter 方法并更改它们,并让其他 classes 可以看到这些更改吗?例如,如果我有另外两个 classes 实现 Runnable 并每隔几秒打印一次 Foo 的字段。我试过了,结果是每个 class 只看到自己的更新,而 none 看到其他 classes'.

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 classes(下面的 Baz 和 Bar)并用 setters 更新了 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}

栏的输出:

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);
        }
    }
}

更新:一些愚蠢的拼写错误

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

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

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

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

您有两个独立的主要方法,因此您可能 运行 将每个 class 都放在一个单独的 JVM 中。而是创建一个 运行 每个 class 在不同线程中的主要方法,并且 运行 那。

您还需要将 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();
    }
}