单例模式是指在一个系统中,一个类有且只有一个对象实例。
单例模式的实现:单例模式从创建方式上又分为饿汉式和懒汉式两种
饿汉模式
直接实例化
// Single.java
package com.company;
public class Single {
//1.将构造方法私有化,不允许外部直接创建对象
private Single(){
}
//2.创建类的唯一实例,类初始化时,立即加载这个对象
private static final Single s1= new Single();
//3.提供一个用于获取实例的方法,使用public static(变成类方法,不然要new后才能使用)修饰
public static Single getInstance(){
return s1;
}
public static void main(String[] args) {
// 测试
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
// 发现s2和s1是同一个
if(s1==s2){
System.out.println("是同一个");
}else {
System.out.println("不是");
}
}
}
枚举形式
package com.company;
/*
* 枚举类型:表示该类型的对象是有限的几个
* 我们可以限定为一个,就成了单例
*/
public enum Single3 {
INSTANCE
}
class Single {
public static void main(String[] args) {
Single3 s = Single3.INSTANCE;
Single3 s2 = Single3.INSTANCE;
System.out.println(s==s2);
}
}
懒汉模式
package com.company;
public class Single2 {
//1.将构造方式私有化,不允许外边直接创建对象
private Single2(){
}
//2.声明类的唯一实例,使用private static修饰
private static Single2 s2;
//3.提供一个用于获取实例的方法,使用public static修饰
public static Single2 getInstance(){
if(s2==null){
s2 = new Single2();
}
return s2;
}
public static void main(String[] args) {
// 测试
Single2 s3 =Single2.getInstance();
Single2 s4 =Single2.getInstance();
if (s3==s4){
System.out.println("yes");
}else {
System.out.println("no");
}
}
}
注意:如上实现的单例模式不是线程安全的,他们只适合于单线程环境下使用。因为多个线程并发环境下,还是会创建多个实例对象。
可以通过synchronized内部锁来解决多线程环境下的线程安全问题。
public static Single2 getInstance(){
if(s2==null){
synchronized (Single.class){
s2 = new Single2();
}
}
return s2;
}
Single2 s2 = new Single2();是个原子操作吗?显然不是。
具体步骤如下三步所示:
1.在堆内存上分配对象的内存空间
2.在堆内存上初始化对象
3.设置s2 指向刚分配的内存地址
第二步和第三步可能会发生重排序,导致引用型变量指向了一个不为null但是也不完整的对象。所以,在多线程下上述的代码会返回一个不完整的对象。我们需要加入一个volatile关键字来禁止指令重排序。
package com.company;
/**
* 多线程环境下的懒汉式单例模式(DCL,双检锁+volatile实现)
* 加入了volatile变量来禁止指令重排序
*/
public class Single2 {
private Single2(){
}
private static volatile Single2 s2;
public static Single2 getInstance(){
if(s2==null){
synchronized (Single.class){
s2 = new Single2();
}
}
return s2;
}
}
单例模式的优点:单例模式保证了一个类在一个系统中有且只有一个对象实例,减少了系统内存和性能的开销。
单例模式的使用场景:创建一个对象需要消耗太多的资源或者在一个系统中不适合创建多个对象实例的情况下,我们可以采用单例模式设计实现。