前言
说起单例模式,应该设计模式中算是代码比较短,其主要作用是保证一个类只有一个实例,节省内存空间,也避免了频繁的创建与销毁对象,比如在spring中bean的创建时,就默认采取了单例模式。单例模式主要包括懒汉式和饿汉式。顾名思义,懒汉式就是“懒”,在你需要的时候我再给你创建对象,而对于饿汉式,则在于“饿”,在类加载时就就生成实例对象。
实现
为了不让外部类创建对象,一般是通过对构造函数的私有化来实现,
懒汉式
public class lazy {
private static volatile lazy instance;
private lazy() {
}
public static lazy newInstance() {
if (null == instance) {
synchronized (lazy.class) {
if (null == instance) {
instance = new lazy();
}
}
}
return instance;
}
}
在学习懒汉式的时候还是遇到一些问题的
关于双重检查锁的问题
以正常的思维,我们开始去写newInstance很容易想成是这样的:
public static lazy newInstance() {
synchronized (lazy.class) {
if (null == instance) {
instance = new lazy();
}
}
return instance;
}
不过如果在这种情况下,就会出现这么一种情况,在大量线程同时调用newInstance()时,就会出现很严重的锁竞争的问题,而改进的办法,就是在同步代码块外部再添加一个null的判断,那么在实例创建好后,之后进来的线程就可以直接返回此实例。
关于volatile变量的问题
在性能上得到优化后,不过在线程安全上还是存在问题。场景是这样的。现在有个线程A和线程B,同时到达synchronized(lazy.class)处,A抢到了锁,执行完后,B获取到了锁,此时若线程A没有将工作内存中的值写回到主内存中,那么此时线程B也创建了一个对象,也就不符合我们所说的“一个类对应一个实例”。这时我们就需要给实例添加一个volatile修饰,使用它的可见性去解决这个问题。说到volatile,还有很多的东西需要去解释,可以看这篇文章。
饿汉式
饿汉式相对较简单
public class hungry {
private static hungry instance = new hungry();
private hungry() { }
public static hungry newInstance() {
return instance;
}
}