JAVA单例模式
单例模式,所有的操作都只由一个对象来处理,确保一个类只有一个实例,并且易于外部访问,节省资源。
在java类实例化的时候,会使用init方法对实例成分(实例变量、实例代码块)初始化,而后是构造函数的调用,这样就创建了一个对象。
如果想要一个对象只有一个实例,那么就只能调用一次init方法,也就是只能使用一次new方法。
饿汉式
在加载类的时候就创建了单例对象
public class Singletonl {
private static Singletonl instance = new Singletonl();
private Singletonl() {} //私有构造方法
public static Singletonl getInstance(){ //不是static方法,无法通过类名.getInstance调用单例
return instance;
}
/*
性能:加载类的时候就创建了实例,节省时间,占据内存(如果该单例占据空间较大且长时间不用,就不是很好)
线程:安全
非延迟加载
*/
}
public class SingleTest {
public static void main(String[] args) {
Singletonl a = Singletonl.getInstance();
System.out.println(a); // Singletonl@14ae5a5
Singletonl aa = new Singletonl(); //报错'Singletonl()' has private access in 'Singletonl'
Singletonl b = Singletonl.getInstance();
System.out.println(a); // Singletonl@14ae5a5
}
}
懒汉式
未加锁
在需要对象的时候才创建,但是线程不安全,当多个线程同时执行到instance == null的判断语句时,会生成多个Singletonl对象,不符合单例的规定。
public class Singletonl {
private static Singletonl instance = null;
private Singletonl() { //私有构造方法
}
public static Singletonl getInstance(){
if (instance == null) {instance = new Singletonl();}
return instance;
}
/*
懒汉式
性能:用的时候加载,节省内存
线程:线程不安全
延迟加载
线程1 线程2
同时判断instance == null
同时执行instance = new Singletonl() 生成两个对象
*/
}
加锁(synchronized)
在需要对象的时候才创建,对getInstance()加锁,可以保证每次只有一个线程去实例化对象,但是当对象存在之后,多线程只能串行去获取对象,效率不高。对于已经存在的单例对象,此时所有线程应该都可以获取到对象,但是锁将多线程变为了串行。
public class Singletonl {
private static Singletonl instance = null;
private Singletonl() { //私有构造方法
}
public synchronized static Singletonl getInstance(){
if (instance == null) {instance = new Singletonl();}
return instance;
}
/*
懒汉式
性能:在多线程的情况下就变成了串行
线程:线程不安全
延迟加载
*/
}
懒汉式+内部加锁(非线程安全)
这种方法也会产生线程不安全的问题,当多个线程执行到判断语句if (instance == null)后,若instance为空,线程a拿到锁,其他线程在等待锁,a初始化对象instance后释放锁,对于其他线程来说,他们由于阻塞到判断语句之后,会认为instance为空,继续初始化,使得单例模式被破坏。
public calss Singletonl{
private static Singletonl instance = null;
private Singletonl() { //私有构造方法
}
public static Singletonl getInstance(){
if (instance == null) {
synchronized (Singletonl.class) {
instance = new Singletonl();
}
}
return instance;
}
/*
懒汉式 + 同步
性能:串行
线程:线程不安全
延迟加载
线程1 线程2
同时判断instance == null
拿到锁 阻塞
new instance 阻塞
释放锁 拿到锁
new instance
*/
}
懒汉式+两次检测(DCL Double-Check-Lock)----不加volatile
DCL会对instance进行两次检测,避免了创建多个instance的问题,但该方法会有空指针的异常,这是由于java的“智能化”自作主张乱序重拍前后不相关的指令,使得instance还没初始化完就赋值给分配的空间,致使其他线程在使用instance对象的时候报空指针异常。
public class Singletonl {
private static Singletonl instance = null;
private Singletonl() { //私有构造方法
}
public static Singletonl getInstance(){
if (instance == null) {
synchronized (Singletonl.class) {
if (instance == null) {
instance = new Singletonl();
}
}
}
return instance;
}
/*
两次检查instance
性能:串行 速度快一些
线程:线程安全 空指针异常
延迟加载
*/
/*
这是由于为了加快程序的运行速度, java会对前后指令不互相依赖的指令乱序执行
java 在new对象的时候,指令顺序为1.分配空间,2.初始化,3.将对象指向空间
乱序有可能执行顺序是312, 因此instance初始化完成后, 为空指针
*/
}
懒汉式+两次检测(DCL Double-Check-Lock)----加volatile
volatile避免了乱序重排指令,因此可以避免空指针异常
public class Singletonl {
private volatile static Singletonl instance = null;
private Singletonl() { //私有构造方法
}
public static Singletonl getInstance(){
if (instance == null) {
synchronized (Singletonl.class) {
if (instance == null) {
instance = new Singletonl(); // 调用初始化方法
}
}
}
return instance;
}
/*
两次检查instance double-check-lock dcl
性能:串行
线程:线程安全 无空指针异常风险
延迟加载
*/
}
静态内部类
jvm隐含的添加了同步机制,在这种情况下,我们就不需要人为的进行同步控制。这些情况为:
- 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时;
- 访问final字段时;
- 在创建线程之前创建对象时;
- 线程可以看见它将要处理的对象时。
因此我们可以通过静态初始化器的方法实现单例模式。
public class Singletonl{
private Singletonl() {} // 构造函数
private static class Holder {
private static Singletonl instance = new Singletonl();
}
public static Singletonl getInstance() {
return Holder.instance;
}
/*
静态内部类, 静态内部类会延迟加载, 在类装载的时候不会立即执行,
其他静态方法或成员会在类装载的时候初始化, 而静态内部类是在调用的时候才初始化
两次检查instance double-check-lock dcl
性能:串行
线程:线程安全 无空指针异常风险
延迟加载
*/
}
枚举单例
非延迟加载单例模式
public enum Singleton {
INSTANCE;
private Singleton() {
}
public static Singleton getInstance() {
return INSTANCE;
}
/*
枚举
线程安全,非延迟加载
*/
}
延迟加载单例模式
public class Singleton {
private enum Holder {
INSTANCE;
private Singleton instance;
private Holder (){
this.instance = new Singleton();
}
}
public static Singleton getInstance() {
return Holder.INSTANCE.instance;
}
/*
饿汉式静态内部类枚举
线程安全,非延迟加载
*/
}