单例模式是一种经典的设计模式,在校招阶段主要考察两个设计模式:
1.单例模式 2.工厂模式
设计模式需要大家有一定的开发经验的积累,才好理解~~
单例 => 单个实例(instance) 对象 (类的实例,就是对象)
单例:一个程序中,某个类,只创建出一个实例(一个对象),不能创建多个对象~~
Java中的单例模式,借助Java语法,保证某个类,只能创建出一个实例,而不能new多次
2.在Java语法中,如何做出单例模式的实现~~
Java中实现单例模式有很多写法,目前我们主要说两种~~
2.1 饿汉模式
2.2 懒汉模式
例如计算机中的例子:打开一个硬盘的文件,读取文件内容,并显示出来~~
饿汉:把文件所有内容都读到内存中,并显示
懒汉模式:只把文件读一小部分,把当前屏幕填充上,如果用户翻页了,再读其他文件,如果不翻页,就生了~~
package threading;
//把这个类设定成单例的~~
class Singleton{
//唯一实例的本体
private static Singleton instance = new Singleton();
//获取到实例的方法
public static Singleton getInstance() {
return instance;
}
//禁止外部new实例
private Singleton(){}
}
public class ThreadDemo17 {
public static void main(String[] args){
//此时s1和s2是同一个对象
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//Singleton s3 = new Singleton();
}
}
private static Singleton instance = new Singleton();
instance 就是唯一的实例了~~
被static修饰,该属性是类的属性~~(类对象上)
JVM中,每个类的类对象只有唯一一份,类对象里的这个成员,自然也是唯一一份了~~
Singleton s3 = new singleton();
c此处需要把new操作给禁止掉!!
把该类的构造方法设为private即可!!
另一方面,单例模式,有一种实现方式,也可以保证,反射安全
wait notify 需要搭配锁对象
单例模式:
懒汉模式来实现单例:核心思想,非必要不创建
懒汉模式实现单例
package threading;
//通过懒汉模式实现一下单例模式
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 ThreadDemo18 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
}
饿汉模式的代码实现
class Singleton {
//唯一实例的本体
private static Singleton instance = new Singleton();
//获取到实例的方法
public static Singleton getInstance() {return instance;}
//禁止外部new实例
private Singleton() {}
}
饿汉模式,认为线程是安全的~~
如何解决上述线程安全问题呢 加锁
public static SingletonLazy getInstance() {
Sychronized (SingletonLazy.class) {
if(instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
保证判定和new是一个原子操作
加锁其实是一个比较低的操作
(加锁就可能涉及到阻塞等待 ) 非必要不加锁~~
其实,此处的线程不安全,只出现在首次创建对象这里,一旦对象new好了,后续调用getInstance,就 只是单纯的读操作,就没有线程安全问题,就没必要加锁了!!
public static singletonLazy getInstance() {
//这个条件,判定是否要加锁,如果对象已经有了,就不毕加锁了
if(instance == null() {
sychronized (SingletonLazy.class) {
if(instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
虽然两个条件相同,但是如果调用的时间间隔长了,结果也可能会不同!!
3.指令重排序
这个操作,可能触发,指令重排序~~
1.创建内存
3.把内存地址,付给引用
2.调用构造方法
(t2将错就错)
改这个问题加 : volatile
小结:
单例模式,线程不安全问题
饿汉模式:天然就是安全的,只是读操作
懒汉模式:不安全的,有读就有写~~
1.加锁:把if和new变成原子操作
2.双重if,减少不必要的加锁操作
3.使用volatile禁止指令重排序,保证后续线程肯定拿到的是完整对象