单例模式:某个类,在进程中只能有一份实例
单例模式分为: 饿汉模式和懒汉模式
饿汉模式
class Singleton {
//饿汉模式
private static Singleton ins = new Singleton();
//为什么要设为静态?
//因为属于类的变量,我们要想只要类一加载,就会创建。
//不使用行不行?
//不可以,因为就不属于类了,就需要通过创建实例,但是我们私有化了构造,所以不行。
private Singleton() {
;//只能创建一个实例的决定性因素在此构造方法是私有的
}
//获取唯一实例
public static Singleton getIns() {
return ins;
}
}
class Main{
public static void main(String[] args) {
Singleton singleton1=new Singleton();//当我们去创建的时候就报错了!
Singleton singleton2=Singleton.getIns();//这是正确的
}
}
饿汉不会导致线程安全问题
懒汉模式
class Singletonlazy{
//懒汉模式
private static volatile Singletonlazy Instance=null;//懒汉模式不着急去new,等程序需要才去new
private Singletonlazy(){
;
}
public static Object obj=new Object();//定义一个锁对象
public static Singletonlazy getInstance (){//获取唯一实例
//这里为什么套两个if ?
//第一个if判断是否要加锁,如果对象有了就不必加锁了
//第二个if判断是否要创建对象
if(Instance==null){
synchronized (obj){
if(Instance==null){
Instance=new Singletonlazy();//懒汉模式下,不能保证唯一性,所以加锁。
}
}
}
return Instance;
}
}
class Main{
public static void main(String[] args) {
Singletonlazy p1= Singletonlazy.getInstance();
Singletonlazy p2= Singletonlazy.getInstance();
System.out.println(p1==p2);//true
}
上述代码中:Instance=new Singletonlazy();可能会发生指令重排序
注:当指令重排序后new操作执行顺序:创建内存->内存地址赋给引用->调用构造方法。
当我们有一个t1线程正在执行:创建内存->内存地址赋给引用时,突然调度给t2线程,t2线程执行时不为null,return走后执行某些操作时,因为我们还没调用构造方法赋值会导致出现问题。(小概率极端情况)
解决办法给引用添加volatile,加上后不优化了,也就不会导致指令重排序也保证了内存可见性问题,加锁也可保证了内存可见性。