目录
一.什么是单例模式
二.饿汉模式和懒汉模式
饿汉模式
代码
懒汉模式
代码
关于多线程安全的问题
如何解决懒汉模式多线程安全问题
双if判断
一.什么是单例模式
简单来说,就是我们在程序中通过代码进行限制,在该程序中
只能创建一个对象
二.饿汉模式和懒汉模式
因为只能创建一个对象,所以对于我们而言就有两种方式
第一种方式
饿汉模式
让程序自动在类中创建唯一的对象
在类外不能被创建
代码
class Student{
//此处在类的内部一开始就自己创建了唯一的Student对象
public static Student st = new Student();
//提供private构造方法,让其在类外不能被调用
private Student(){}
//提供getInstance方法,以便获取唯一的Student对象
public static Student getInstance(){
return st;
}
}
public class Main {
public static void main(String[] args) {
//此处的st2,和st3都指向唯一的st对象,还是只存在唯一一个对象
Student st2 = Student.getInstance();
Student st3 = Student.getInstance();
}
}
第二种方式
懒汉模式
就是程序在自己内部不创建唯一对象
而是我们需要的时候再在类外面new那个唯一的对象
这种方式就叫做懒汉模式
代码
class Teacher{
//将te指向空,等到get的时候再进行实例化,这里是没有指向任何对象
private static Teacher te = null;
//避免通过new的方式创建其他实例,只能通过get方式获取唯一实例
private Teacher(){}
//通过get创建唯一对象
public static Teacher getInstance(){
if(te == null){
te = new Teacher();
}
return te;
}
}
public class demo1 {
public static void main(String[] args) {
//调用才创建对象
Teacher te1 = Teacher.getInstance();
}
}
关于多线程安全的问题
其实我们大体上就要记住一点
多线程同时修改同一份变量大概率就会发生线程安全的问题
所以在上述代码中
我们的饿汉模式在多线程的情况下
他不管调用几次都只是在返回已经创建好的那个唯一对象
只是相当于获取
并没有进行任何修改操作
但是在懒汉模式的多线程下
我们是进行了判断,然后创建的唯一对象,创建这个操作就是对数据进行了修改
那么多个线程同时对一个数据进行修改,那么就会发生多线程安全的问题
所以
饿汉模式没有多线程安全问题
懒汉模式有多线程安全问题
如何解决懒汉模式多线程安全问题
加锁
加锁
还是(* * *)加锁
我们最优先想到的就是直接在get方法那里加上synchronized
class Teacher{
private static Teacher te = null;
private Teacher(){}
//直接加上一把锁
public static synchronized Teacher getInstance(){
if(te == null){
te = new Teacher();
}
return te;
}
}
但是这样是会有问题的
因为此处的操作并不是原子性,那么如果是多线程进行操作,可能会出现下面的情况
也就是说,我们可能会同时返回两个对象回去
为了避免这种情况,我们还需要加入关键字volatile(禁止指令重排序与保证可见性)
class Teacher{
//这里加上volatile 后就没有排序的问题了
private static volatile Teacher te = null;
private Teacher(){}
public static synchronized Teacher getInstance(){
if(te == null){
te = new Teacher();
}
return te;
}
}
实际上到了这一步
我们已经解决安全的问题了
但是为了进一步提升效率,我们采取了双if判断的方式
双if判断
我们把锁加在了if判断语句这里,而取消了get方法的锁
让多个线程同时调用该方法,如果没有被创建,则执行上锁的new对象操作
如果已经被创建则返回该对象
class Teacher{
private static volatile Teacher te = null;
private Teacher(){}
public static Teacher getInstance(){
//第一个if判断,如果已经创建锁,就直接跳到最后的返回
if(te == null){
//多个线程在调用get的时候,都卡在这里竞争这把锁
//竞争成功的创建唯一对象,并释放锁
//其他线程拿到被释放的锁之后进去查看,发现第一个线程已经创建了唯一对象
//不满足第二个if条件,又跳到最后返回
synchronized(Teacher.class){
if(te == null){
te = new Teacher();
}
}
}
return te;
}
}
但我个人感觉,用前面的volatile和对get方式加锁已经足够了
虽然效率比双if要低一些但已经解决了我们的多线程安全问题
双if判断只能说锦上添花吧