单例模式的意图是为了确保一个类在整个JVM中有且仅有一个实例,并为他提供一个全局的访问点。
单例模式机制
怎么样才能阻止其他开发人员创建类的新实例?
可以创建唯一一个构造函数,并设置为私有。
单例和线程
多线程延迟初始化一个单例模式尽量避免多个线程同时初始化该单例对象。
假设一个线程发现该单例对象为null,接着第二个线程运行也发现该单例对象为null,然后两个线程都会对该单例对象进行初始化,为了避免这种竞争,需要使用锁机制去协调不同线程对同一个方法的执行。
public class Singleton{
//持有私有静态实例,防止被引用,此处赋值null,目的是实现延迟加载
private static Singleton instance=null;
//构造私有构造方法,防止被实例化
private Singleton(){}
//静态工程方法,创建实例
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
此单例模式没有线程安全,放在多线程环境中,一般只有在第一次创建对象的时候需要加锁代码如下:
public static Singleton getInstance(){
if(instance == null){
synchronized(instance){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
此种情况可能出现问题,因为Java指令中创建对象和赋值是分开操作,也就是说instance = new Singleton()是分两步操作的。
例如有A、B两个线程:
1、A、B两个线程同时进入到了if判断,随机到A线程先进行if判断
2、此时instance为null,执行instance = new SingleTon()操作,由于JVM内部优化机制,JVM先分配给SingleTon实例的一些空白内存,并赋给instance成员(此时JVM并没有实例化SingleTon),然后A离开synchronized
3、然后B线程计入synchronized模块,由于instance≠null,他会立马离开程序块,并将程序结果返回给该方法的程序。
4、此时B线程打算使用SingleTon实例,却发现报错了,他其实并没有初始化
于是进一步优化:
public static class SingleTonFactory(){
private static SingleTon instance = new SingleTon();
public static SingleTon getInstance(){
return SingleTonFactory.instance;
}
}
此时SingleTon只会被实例化一次
在JVM中只有一个实例存在,对于某些需要频繁创建的类,在大型系统中节省了大量开销;省去了new操作,降低系统内存的利用率和GC的压力;控制核心交易引擎,防止系统错乱,保证核心服务器独立控制整个流程。