实现单例模式的思路
(⼀个类能返回对象⼀个引⽤(永远是同⼀个)和⼀个获得该实例的⽅法(必须是静态⽅法,通常使⽤getInstance这个名称);当我们调⽤这个⽅法时,如果类持有的引⽤不为空就返回这个引⽤,如果类保持的引⽤为空就创建该类的实例并将实例的引⽤赋予该类保持的引⽤;同时我们还将该类的构造函数定义为私有⽅法,这样其他处的代码就⽆法通过调⽤该类的构造函数来实例化该类的对象,只有通过该类提供的静态⽅法来得到该类的唯⼀实例)
如果像如下只私有化构造器的话,就可以任意的给他赋值,所以只能通过提供静态方法的方式来获取该类的实例
@RequestMapping("/test24")
public String test24(HttpServletRequest httpServletRequest) throws InterruptedException {
GiantDragon.instanceqwe=null;
System.out.println();
return null;
}
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
public static GiantDragon instance =new GiantDragon();
}
懒汉式代码案例:
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象
private static GiantDragon instance;
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance() throws InterruptedException {
if (instance == null) {
instance = new GiantDragon();
}
return instance;
}
}
饿汉式代码案例
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象
private static GiantDragon instance=new GiantDragon();
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance() {
return instance;
}
}
通过对代码的分析,我们可以发现饿汉式单例模式是线程安全的,由于饿汉式单例模式在加载时已经创建了对象,这就保证了在其他线程再次访问时将会访问到同⼀个对象。⽽上述的懒汉单例模式是线程不安全,如下图所⽰,如果在线程1发现instance为空后,线程2抢占到了执⾏权,也进⾏了判断,接着两个线程都发现instance为空,将会创建两个不同的instance对象返回,所以这就不能保证单例模式,即不是线程安全的。
测试以上说法:
测试方法分别用浏览器和post请求下面这个接口,模拟两个线程,手速不够快的话,可以让线程sleep一会
Thread.sleep(2000);
@RequestMapping("/test25")
public String test25(HttpServletRequest httpServletRequest) throws InterruptedException {
GiantDragon giantDragon1= GiantDragon.getInstance();
System.out.println(giantDragon1);
return null;
}
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance;
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance() throws InterruptedException {
if (instance == null) {
Thread.sleep(2000);
instance = new GiantDragon();
}
return instance;
}
}
可以看出两个请求得到了不同的对象。
解决方法线程加锁
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance;
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance() throws InterruptedException {
//外层判断是否为null,防止后续线程都要加锁影响性能
if (instance == null) {
synchronized (GiantDragon.class) {
System.out.println("进入锁内!");
if (instance == null) {
Thread.sleep(2000);
System.out.println("生成实例!");
instance = new GiantDragon();
}else {
System.out.println("第二次执行!");
}
}
}
return instance;
}
}
执行结果
或者可以这样,哈哈哈,不太严谨,玩玩就行
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon() {
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance;
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance() throws InterruptedException {
if (instance == null) {
Thread.sleep(2000);
instance = new GiantDragon();
}
Thread.sleep(2000);
return instance;
}
}