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();
}
}
想弄清楚单例模式。
如果我实现如下所示的单例模式,我该怎么做才能使其他 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();
}
}