单例模式是什么?为什么需要单例模式
单例模式顾名思义,就是在整个运行时域,一个类只有一个实例对象。
为什么需要单例模式,因为有的类实例对象创建与销毁资源消耗不大,比如String,有的类庞大复杂,如果频繁创建销毁会造成不必要的浪费。
比如创建数据库连接对象Connection con,可以使用单例模式,避免重复创建销毁。
饿汉模式
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
这种模式是在类加载的同时就进行实例化了,避免了多线程的同步问题。当然缺点也是有的,因为类加载时就实例化了,没有达到Lazy Loading (懒加载) 的效果,如果该实例没被使用,内存就浪费了。
懒汉模式
懒汉模式顾名思义就是在需要这个类的时候在进行实例化,而不是在加载就实例化
一个最基本的懒汉模式:
一
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {//此处线程不安全
instance = new Singleton();
}
return instance;
}
}
这种写法是线程不安全的,比如AB两个线程同时,在同时进行判空,那么会实例化多次。
如何解决呢?可以打上synchronized,变成如下
二
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
打上synchronzed后只有一个线程可以进入getinstance方法。随之而来的问题就是,我们需要在构建对象的时候进行同步加锁操作,而此写法在每次获取对象的时候都加上锁了,对性能影响很大。所以不可取
三
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class);
instance = new Singleton();
}
return instance;
}
}
这种情况下在当前A线程获得锁的线程后可能有其他如B线程也在等待进入这个Class锁,A线程获取锁后创建实例,然后释放锁,之后等待池中的B线程获得锁,然后就会产生创建两个对象的错误情况
四
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class);
if (singleton == null) {
instance = new Singleton();//线程不安全
}
}
return instance;
}
}
因为hanpens-before原则,实例化对象有三部:分配内存、初始化对象、将对象指向内存。在执行的时候为了效率会进行指令重排,多线程下会乱套。
五
public class Singleton {
private volatile static Singleton instance;//加入volatile
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class);
if (singleton == null) {
instance = new Singleton();
}
}
return instance;
}
}
这样就完美了!