单例模式是一种设计模式,就像下象棋的时候的棋谱一样,我们写代码的时候可以按照这种模式的写法去照猫画虎
单例是指什么的?
单例指的就是单个实例,实例就是对象,就是一个类里面的实际的例子。一个程序中的一个类只能创建一个对象,不能new多个对象
单例模式就是借助Java语法保证一个类里面只创建出一个实例对象
这里我主要介绍两种模式:饿汉模式和懒汉模式
饿汉模式
//饿汉模式的单例设计模式
class Singleton {
//被static修饰说明是类的属性(在类对象上),在JVM里,每个类的类对象只有唯一一份,所以类对象的这个成员也是唯一的
private static Singleton instance = new Singleton();
//getInstance()获取实例的方法 一般是static的
public static Singleton getInstance() {
return instance;
}
//将构造方法设置为私有的就可以保证出一个类里只能出现一个实例对象
private Singleton() {}
}
public class ThreadDemo15 {
public static void main(String[] args) {
//s1和s2是同一个对象
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
Singleton s3 = new Singleton(); //编译报错,因为设置类为单例的,所以无法再创建出新的对象
}
}
关于getInstance():
getInstance()这个方法在单例模式中用的甚多,为了避免对内存造成浪费,直到需要实例化该类的时候才将其实例化,用getInstance() 来获取该对象,这样不用每次都用new关键字,就不必一执行这个类就初始化,这样可以防止数据冲突,节省内存空间
线程安全问题:
饿汉模式是线程安全的,因为我们看到,对于getInstance()这个代码块来说,饿汉模式只有一个读操作,读操作不会引发线程安全问题。
饿汉模式
//懒汉模式实现单例
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) { //调用getInstance()的时候才实例化对象
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {}
}
public class ThreadDemo16 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
}
}
线程安全问题:
1.加锁:懒汉模式里面有修改操作,他要先判断instance是否为空,然后再进行实例化对象,这两个步骤不是原子的,所以我们要通 过加锁来解决这个线程安全问题
2.防止指令重排序:由于实例化对象这个操作实际上是三条指令,
1.创建内存空间
2.调用构造方法
3.把内存地址交给引用
所以我们要加上volatile关键字来防止指令重排序
加上线程安全之后的代码如下:
//懒汉模式实现单例
class SingletonLazy {
volatile private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if (instance == null) {
synchronized(SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() {}
}
public class ThreadDemo16 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
}
}
小结:
单例模式的线程安全问题
饿汉模式是安全的,只有读操作
懒汉模式不安全,有读有写
解决方法:《经典面试题》
1.给写操作加锁,把if和new变成原子操作
2.双重if
3.用volatile禁止指令重排序,保证后续线程拿到的对象是完整的