我认为您必须区分已经存储在内存中的内容和代码执行。
在单例对象中,您有:
- 字段:它们存储在内存中。它们可以在多个线程之间共享,并且您无法保证它们会保持一致(除非您使它们同步)。
- 要调用的方法:可以从多个线程调用它们。每次执行都是独立且线程安全的,除非它们不正确地访问某些共享字段。
现在来回答您的问题:如果您在多个线程之间共享您的单例对象并同时访问它,则每个线程都将执行单例对象的代码部分,并包含在自己的执行中。
此外,如果您编写一个Thread.currentThread().getId();
基本上返回您正在执行的单例方法的线程 ID,您将获得不同的 id,因为不同的线程正在执行它们自己的方法堆栈。无状态意味着您在单例中没有字段可以在它们之间共享!
关于无状态和有状态的一句话
无状态意味着 bean 没有任何可修改的字段来共享。这意味着您的对象中只有方法或/和静态内容,因此您可以在任何地方使用它们并且始终返回相同的结果。您不必担心同步对字段的访问。
这是一个关于stateless的基本示例,假设您有一个只执行求和操作的类:
public class StatelessClass{
public int sum(int a, int b){
return a+b;
}
}
同样,您可以将其声明为抽象类(本身不可实例化)并使其方法为static,这意味着您不需要它的任何实例来调用其方法:
public abstract class StatelessClass{
/**
* I only sum objects
*/
public static int sum(int a, int b){
return a+b;
}
}
然后您可以将其用作,这实际上与拥有一个SingletonStatelessClass.sum(1,1);
对象本身非常相似,不同之处在于在 Singleton 中您有一个在应用程序中共享的唯一实例。
以同样的方式,拥有一个注入并提供对服务的访问的字段都不被认为会改变对象的状态:
public class StatelessServiceClass{
private Service service;
public int sum(int a, int b){
return service.sum(a,b);
}
public void setService(Service serv){
this.service=serv;
}
}
但是,拥有一个可修改的字段会使 Object有状态:
public class StatefulClass{
//This fields make the object STATEFUL
private int totalSum = 0;
public int sum(int a, int b){
int sum = a + b;
totalSum = totalSum + sum;
if (totalSum > 100)
System.out.println("This thread "+Thread.currentThread().getId()+
+" got it!");
return sum;
}
}
由于sum
可以同时被多个线程访问,所以应该保证totalSum
以同步的方式访问。除非您这样做,否则不能保证打印的句子是真实的。
@BalusC在此答案的Threadsafety
文章中也正确解释了所有这些。