单例设计模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
两种模式
- 饿汉式
- 懒汉式
1.1 饿汉式
一个类只能创建一个对象
- 私有化构造器 private
- 在类的内部创建一个类的实例,且为static
- 私有化对象,通过公共方法调用
- 此公共方法只能通过类来调用,因为设置的是static,同时类的实例也是static
//饿汉式
class Single
{
//私有化构造器 private
private Single() {}
//在类的内部创建一个类的实例,且为static
private static final Single s = new Single();
//私有化对象,通过公共方法调用
public static Single getInstance()
{
return s;
}
}
这是单例模式,一般用于比较大,复杂的对象,只初始化一次,有一个private的构造函数,使得不能用new来实例化对象,只能调用getInstance方法来得到对象,而getInstance保证了每次调用都返回相同的对象。
new 和 getInstance区别
1. new的使用:
如Object _object = new Object(),这时候,就必须要知道有第二个Object的存在,而第二个Object也常常是在当前的应用程序域中的,可以被直接调用的
简单来说,new就是通过生产一个新的实例对象,或者在栈上声明一个对象,每部分的调用,用的都是一个新的对象。
2. GetInstance的使用:
* 在主函数开始时调用,返回一个实例化对象,此对象是static的,在内存中保留着它的引用,即内存中有一块区域专门用来存放静态方法和变量,可以直接使用,调用多次返回同一个对象。
* 对象使用之前通过getInstance得到而不需要自己定义,用完之后不需要delete;
* new 一定要生成一个新对象,分配内存;getInstance() 则不一定要再次创建,它可以把一个已存在的引用给你使用,这在效能上优于new;
* new创建后只能当次使用,而getInstance()可以跨栈区域使用,或者远程跨区域使用。所以getInstance()通常是创建static静态实例方法的。
* getInstance这个方法在单例模式用的甚多,为了避免对内存造成浪费,直到需要实例化该类的时候才将其实例化,所以getInstance来获取该对象。
* 注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
1.2 懒汉式
- 私有化构造器
- 创建一个私有的实例static 先不实例化为 null
- 通过公共方法调用 static 在方法里面进行判断,if = null 实例化 !=null 直接return
class Single{
//1.私有化构造器
private Single() {}
//2.创建一个私有的实例为static 且值设置为null
private static Single s=null;
//3.通过公共方法调用,static
public static Single getInstance()
if(s==null)
{
s=new Single();
}
return s;
}
}
2.1 线程安全问题
-
饿汉式:是安全的,可以直接用于多线程而不会出现问题。
-
懒汉式:是不安全的,可以使用同步锁synchronized解决。
2.2 解决多线程中懒汉式的不安全问题
不安全情况:
//懒汉式
class Single
{
private static Single s=null;
private Single() {}
public static Single getInstance()
{
if(s==null)
{
//--A A线程进入
//--B B线程进入
/*由于s==null判断一直为真,线程可以一直进入
此时线程不安全,不是单例模式。
*/
s=new Single();
}
}
return s;
}
解决办法:加入同步锁
//懒汉式
class Single
{
private static Single s=null;
private Single() {}
public static Single getInstance()
{
if(s==null)
{
synchronized (Single.class) {
if(s==null)
{
//--A
/* 使用synchronized可以在一个线程A进入后关闭入口
而当一个线程A进入后s!=null,实现单例模式
此处使用双重if判断,可以减少判断锁的次数,
提高了懒汉式的效率。
*/
s=new Single();
}
}
}
}
return s;
}
饿汉式和懒汉式的对比
- 饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
- 懒汉式,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。