单例模式是结构最简单的设计模式,核心结构只包含一个特殊类即单例类。通过单例模式可以确保系统中的一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数进行控制,节约系统资源。
单例模式的目的是保证系统中一个类有且只有一个实例,并提供一个访问它的全局访问点。
常见单例模式实现:
1.饿汉式单例模式(在类加载时就已创建单例对象)
在类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,创建单例类的唯一实例。
饿汉式在类加载时就已实例化,无须考虑多线程访问,但不能实现延迟加载,不管用不用都会占用内存。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
2.双重检查加锁的懒汉式单例(延迟加载,在调用时才会去实例化)
懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但出现多线程同时首次引用的几率变得比较大,需要通过双重检查加锁等机制来控制,将导致系统性能受到一定影响。
使用双重检查加锁的懒汉式单例模式,需要在静态成员变量instance之前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能正确处理,但volatile关键字会屏蔽Java 虚拟机所做的一些代码优化。
public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
// 第一重检查
if (instance == null) {
// 加锁
synchronized (LazySingleton.class) {
// 第二重检查
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
3.静态内部类单例模式(实现延迟加载,又可以保证线程安全,不影响系统性能)
由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance, 此时会首先初始化这个成员变量,由java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。 由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。
public class Singleton {
private Singleton() {
}
//静态内部类
public static class HolderClass{
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return HolderClass.instance;
}
}
4.test
/**
* Created by ZhangCheng on ${currentDate:date('yyyy/MM/dd')}
*/
public class Test {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
//引用数据类型:当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址(确切的说,是堆内存地址)
if(s1==s2) {
System.out.println("实例为同一实例!");
}
}
}