单例模式
介绍: 单例模式,是采取一定方法在,整个系统中,对某个类只能存在一个对象实例,并只提供一个获取其对象的实例
饿汉式(静态常量)
//饿汉式(静态常量)
class Test {
// 1.构造器私有化
private Test() {
}
//2.使用组合方式,构建此类的实例
private final static Test test = new Test();
//3.公共接口提供方法创建
public static Test getInstance() {
return test;
}
}
优点:简单,在类加载时候就完成了实例化,解决了线程之间的同步问题
缺点:在类加载的时候就完成了实例化(有可能不是调用了getInstance方法),不能确定没有其他方式导致加载,造成了内存浪费
饿汉式(静态代码块)
//饿汉式(静态代码块)
class Test1 {
// 1.构造器私有化
private Test1() {
}
//2.不加final(否则一开始就要赋值,或者在静态代码块,构造函数(只适用于非静态常量)初始化,),注意这里加不加final都是可以的
private static Test1 test1;
//构建此类的实例(在静态代码块)
static{
test1 = new Test1();
}
//3.公共接口提供方法创建
public static Test1 getInstance() {
return test1;
}
}
优缺点同上(只不过是变量初始化的方式不同)
懒汉式(线程不安全)
**懒汉式与饿汉式不同在:饿汉式是类加载直接构建的惟一的对象,而懒汉式是在调用其对外暴露的方法时才进行构建实例
注意:线程不安全是在if判断时发生的,当线程一与线程二同时判断出test3为空时,将会创建两个对象
**
//懒汉式(线程不安全)
class Test3{
//表示此对象
private static Test3 test3;
//私有化构造对象
private Test3(){
}
//提供公共方法构造
public static Test3 getInstance(){
if(test3 == null){
test3 = new Test3();
}
return test3;
}
}
优点:起到了懒加载效果(什么时候用到什么时候加载)
缺点:只能在单线程下使用
** 不推荐**
懒汉式(线程安全)
//懒汉式(线程安全)
class Test4{
//表示此对象
private static Test4 test4;
//私有化构造对象
private Test4(){
}
//加入静态同步代码块将改类加锁,在调用该方法前,需要获得内置锁
//再多线程下保证其可见性
// (释放锁的时候会刷新主内存,其他线程在获取锁的时候会强制从主内存中读取,)和原子性
public static synchronized Test4 getInstance(){
if(test4 == null){
test4 = new Test4();
}
return test4;
}
}
优点:解决了线程不安全问题
缺点:效率太低了,每次要获取实例时都要进行同步,其实只执行一次即可
** 不推荐**
懒汉式(双重检索模式)
注意:volatile主要是保证了test5新建对象的有序性,在新建对象时分为三步 1.创建实例分配内存 2.调用构造方法,为属性赋值,初始化 3.将其引用给变量
该作用主要是预防第一个if判断 直接执行了3步骤(给变量赋值引用)而未执行初始化
//懒汉式(双重检索模式dcl)
class Test5{
//唯一实例,保证有序性,可见性
private volatile static Test5 test5;
//私有化构造方法
private Test5(){}
//提供方法
public static Test5 getInstance(){
//使用voliatille 防止直接执行3步骤,而没有执行2步骤
if(test5 == null){
//锁住其类对象,在调用代码块时需要获取锁
synchronized (Test5.class){
if(test5==null){
test5=new Test5();
}
}
}
return test5;
}
}
** 该方法完美解绝了问题**
静态内部类
原理:在主类加载时,内部类并不会加载因此可以通过调用方法创建内部类(类在加载的时候,就保证了线程安全)
//静态内部类实现
class Test6{
private Test6(){
}
//通过内部类来加载外类实例
private static class singleTest6{
private static Test6 TEST6 =new Test6();
}
//只有在调用此方法时,该内部类才会被加载
public Test6 getInstance(){
return singleTest6.TEST6;
}
}
优点:通过内部类加载机制实现了懒加载,并且根据类在加载的时候是安全的解决了多线程问题
推荐使用
枚举
在枚举中实例只会被创建一次
//枚举实现
enum Test7{
INSTANCE;
}
优点:解决了懒加载,及线程安全和序列化安全问题
推荐使用
主要推荐使用的模式
饿汉式,懒汉式(双重检索模式),枚举方式,静态内部类
单例模式注意事项及细节
-
单例模式保证了系统内存中只存在一个对象,对于一些需要频繁创建销毁的对象,使用该模式可以提高系统性能。
-
实例化单例对象的时候,要使用相应获取对象的方法,而不是使用new
-
单例模式使用的场景:需要频繁创建和销毁的对象,创建对象时耗时过多(重量级对象)。但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(数据库,session工厂)