文章目录
- 1、什么是单例模式
- 2、饿汉模式
- 3、懒汉模式
1、什么是单例模式
单例模式是一种设计模式。
什么是设计模式呢?设计模式就是一种模板,就像做饭的时候,新手做饭什么调料都是一把放进锅里,有的菜就讲究调料的先后顺序,这时候把做这道菜的调料顺序告诉新手,新手按照这个顺序去做菜,就可以做出味道好的菜。而放调料的顺序就是设计模式, 它是由有经验的人总结完出来的,为的就是让后来的人能更方便的使用,按照他们写的这个方法做,可以少走很多弯路。
其实设计模式本质上就是“规则”。
单例模式又可以分为两种:饿汉模式和懒汉模式。 单例模式能够保证某个类在程序中只存在唯一 一份实例,而不会创建出多份实例。
2、饿汉模式
饿汉模式的核心是类加载的同时就创建了实例。
//饿汉模式下的单例模式
class single{
private static single hungry = new single();//类加载时期就已经创建好对象,且只能创建一次
public static single getInstance(){
return hungry;
}
private single(){
}//禁止外部new对象
}
注意: 1. static是让当前hungry 属性是类属性了,类属性是长在类对象上的,类对象又是唯一实例的(只是在类加载阶段被创建出的一个实例);
2. 构造方法设为private,外面的代码无法从外部new对象;
3. 类对象本身和static没什么关系,只是因为在类里面使用static修饰的成员会作为类的属性,也就是这个属性对应的内存空间是在类对象里面
举个例子:
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
a1.x = 10;
a2.x = 20;
a1.y = 100;
a2.y = 200;
System.out.println(a1.x);
System.out.println(a2.x);
System.out.println(a1.y);
System.out.println(a2.y);
}
}
class A {
int x;
static int y;
结果:
我们会发现,不同的实例对象去修改静态属性的变量,发现最终结果都一样;
3、懒汉模式
懒汉模式的核心是什么时候用到对象什么时候在创建对象。
看代码:
版本一:
//懒汉模式下的单例模式
class singleLazy{
private volatile static singleLazy singlelazy= null;//将对象初始化为空
public static singleLazy getInstance(){ //用static保证对象只能创建一次
if(singlelazy == null){
singlelazy = new singleLazy();
}
return singlelazy;
}
}
但是如果这样写,懒汉模式是不安全的,因为上述代码涉及到两个操作:
当同一个线程有两个操作的时候他就存在线程安全问题!
所以,此时我们就要加锁来保证读和写是原子性的
版本二:
class singleLazy{
private static singleLazy singlelazy= null;//将对象初始化为空
public static singleLazy getInstance(){ //,用synchronized保证线程安全并且对象只能创建一次
//只要进入getInstance就加锁
synchronized (singleLazy.class){
if(singlelazy == null){
singlelazy = new singleLazy();
}
}
return singlelazy;
}
}
上面的代码确实保证了线程安全,但是加锁也是有开销的,除了第一次要创建实例以外,后序再次调用getInstance,此时singlelazy一定都是非空的,再去加锁就浪费了大量的开销,所以我们应该直接返回;
看代码:版本三
class singleLazy{
private static singleLazy singlelazy= null;//将对象初始化为空
public static singleLazy getInstance(){
if(singlelazy == null){//一旦不为空,就说明已经new过对象了,就直接返回
synchronized (singleLazy.class){ //用synchronized保证线程安全并且对象只能创建一次
if(singlelazy == null){
singlelazy = new singleLazy();
}
}
}
return singlelazy;
}
}
调试一下:
public class Test1 {
public static void main(String[] args) {
singleLazy s1 = singleLazy.getInstance();
singleLazy s2 = singleLazy.getInstance();//利用singlelazy new了两个对象
System.out.println(s1 == s2);//true
}
}
结果:
结果两个对象的地址是相同的,所以他们就是一个对象,new第二个对象的时候,发现已经new过对象了,所以直接将s1返回了;
但是这样写代码还是会有一个bug,但是是在 多线程环境下才有的问题,new对象有三个步骤:
所以此时我们就需要用到一个非常重要的关键字,volatile他有两个非常重要的作用。1、解决内存可见性的问题,2、禁止指令重排序。
看代码:
class singleLazy{
private volatile static singleLazy singlelazy= null;//将对象初始化为空
public static singleLazy getInstance(){ //,用synchronized保证线程安全并且对象只能创建一次
if(singlelazy == null){//一旦不为空,就说明已经new过对象了,就直接返回
synchronized (singleLazy.class){
if(singlelazy == null){
singlelazy = new singleLazy();
}
}
}
return singlelazy;
}
}