单例模式(Singleton Pattern)负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。也属于创建型模式。
直接代码实现:
一、饿汉式
package com.xing.design.singleton;
/**
* 单例模式
* @author xing
*/
public class SingletonObj {
//创建 SingletonObj 的一个对象
private static SingletonObj instance = new SingletonObj();
//构造方法私有化
private SingletonObj(){}
//对外提供访问入口,因为关闭了构造方法,所以只能从这个入口访问本类对象
public static SingletonObj getInstance(){
return instance;
}
//写个方法用来调用
public void test() {
System.out.println("单例模式实例化对象的测试方法。。。");
}
}
测试:
package com.xing.design.singleton;
public class SingletonDemo {
public static void main(String[] args) {
SingletonObj singletonObj1 = SingletonObj.getInstance();
singletonObj1.test();
//再来一个对比下hashCode
SingletonObj singletonObj2 = SingletonObj.getInstance();
singletonObj2.test();
System.out.println("singletonObj1=>"+singletonObj1.hashCode());
System.out.println("singletonObj2=>"+singletonObj2.hashCode());
}
}
结果:
图片
可以看到两次获取的对象hashCode码一样,是同一个对象哈。
上述这种写法是在类加载的时候就实例化了对象,这种写法叫饿汉式。但是如果这个对象一直没有用,就造成了内存浪费,没有达到懒加载(Lazy Loading)的效果。
我理解的懒汉式和饿汉式:
懒汉式是用的时候再实例化,饿了再做饭。或者有句话叫啥来着,想蹲坑了才修厕所呢,哈哈哈
饿汉式是先实例化,用的时候直接取用。就跟饿过的人一样,先把饭做好,饿了就能吃。
二、懒汉式
package com.xing.design.singleton;
/**
* 单例模式
* @author xing
*/
public class SingletonObj2 {
//创建 SingletonObj 的一个对象
private static SingletonObj2 instance;
//构造方法私有化
private SingletonObj2(){
System.out.println("构造个SingletonObj2");
}
//对外提供访问入口,因为关闭了构造方法,所以只能从这个入口访问本类对象
public static SingletonObj2 getInstance(){
if(instance == null) {
instance = new SingletonObj2();
}
return instance;
}
public void test() {
System.out.println("单例模式实例化对象的测试方法。。。");
}
}
测试:
package com.xing.design.singleton;
public class SingletonDemo {
public static void main(String[] args) {
SingletonObj2 singletonObj21 = SingletonObj2.getInstance();
singletonObj21.test();
//再来一个对比下hashCode
SingletonObj2 singletonObj22 = SingletonObj2.getInstance();
singletonObj22.test();
System.out.println("singletonObj21=>"+singletonObj21.hashCode());
System.out.println("singletonObj22=>"+singletonObj22.hashCode());
}
}
结果:
图片
这种写法起到了Lazy Loading的效果,不过main方法调用的时候都是第一次加载类哈,看不出来,不过倒是能看出来获取两个实例对象只调用过一次构造哈,更能说明单例模式多次返回的都是同一个对象。
三、双重检验管控的线程安全单例模式
package com.xing.design.singleton;
/**
* 懒汉模式-线程安全,适用于多线程
*/
public class SingletonObj3{
private static volatile SingletonObj3 safeSingleton;//防止指令重排
private SingletonObj3() {
System.out.println("构造个SingletonObj3");
}
public static SingletonObj3 getInstance(){
if(safeSingleton==null){
synchronized (SingletonObj3.class){
if(safeSingleton==null){//双重检测
safeSingleton = new SingletonObj3();
}
}
}
return safeSingleton;
}
public void test() {
System.out.println("单例模式实例化对象的测试方法。。。");
}
}
重排序是为了优化性能,但是不管怎么重排序,在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序,影响结果。具体底层的我也不懂,以后再学吧。这里要禁用重排序,就用了volatile 关键字。
总结:
单例模式只能由自己创建并始终只有一个实例化对象,核心代码是构造方法私有化、提供一个静态方法作为唯一访问入口。
应用场景有:当前系统登录人数管理、Java的Runtime、线程池等频繁的创建和销毁对象的情况。
END