package com.innext.n.xyh;
/**
* @Author: baiyongchen
* @CreateDate: 2019/3/15 15:51
* @Description: 单例几种类型,缺点比较。最终实现方案
*/
public class Person {
//私有化保证外部不能随便通过new创建对象
private Person() {
}
//region 饿汉式
/**
* 静态成员变量mPerson1在Person这个类加载的时候就初始化,创建了Person对象,
* 并且只存在一个;通过Person.getInstance1()方法可以获取该实例对象
*/
private static Person mPerson1 = new Person();
public static Person getInstance1() {
return mPerson1;
}
//缺点:Person对象会一直存在。
//endregion
//region 懒汉式
/**
* 默认不创建对象,当调用getInstance2()时,创建对象。
*/
private static Person mPerson2;
public static Person getInstance2() {
if (mPerson2 == null) {
mPerson2 = new Person();
}
return mPerson2;
}
//缺点:多线程会创建多个对象
//endregion
//region 懒汉式升级版1
/**
* 给getInstance3方法加了关键字synchronized,保证创建对象的时候只有一个调用者
*/
private static Person mPerson3;
public synchronized static Person getInstance3() {
if (mPerson3 == null) {
mPerson3 = new Person();
}
return mPerson3;
}
//缺点:锁会导致时间开销大。
//endregion
//region 懒汉式升级版2
/**
* 给mPerson4 = new Person()语句加锁synchronized,
*/
private static Person mPerson4;
public static Person getInstance4() {
if (mPerson4 == null) {
synchronized (Person.class) {
mPerson4 = new Person();
}
}
return mPerson4;
}
//缺点:设现有线程A和B,在某个时刻两个线程都通过了判空语句但都没有取到锁资源,
// 然后线程A先取得锁资源进入临界区(被锁的代码块),创建了一个对象,然后退
// 出临界区,释放锁资源。接着线程B取得锁资源进入临界区,开始创建对象,退
// 出临界区,释放锁资源,这个时候对象也会出现多个
//endregion
//region 懒汉式升级版3
/**
* 双重校验锁
*/
private static volatile Person mPerson5;
public static Person getInstance5() {
if (mPerson5 == null) {
synchronized (Person.class) {
if (mPerson5 == null) {
mPerson5 = new Person();
}
}
}
return mPerson5;
}
/*
* 首先,方法锁改成代码块锁,减少锁的范围;其次第一次判空,在单线程的情况下提升了效率,但此时如果同时存在两个线程并发情况,即都判空成功,
* 接下来会由锁内的第二次判空来过滤。还是刚才的例子,假设现有线程A和B,在某个时刻两个线程都通过了第一次判空语句但都没有取到锁资源。然后线
* 程A先取得锁资源进入临界区(被锁的代码块),执行第二次判空语句,判空成功,创建了一个对象,然后退出临界区,释放锁资源。接着线程B取得锁资源
* 进入临界区,执行判空语句发现不通过,直接退出临界区,释放锁资源。
*
* 另外在成员变量mPerson5前面加了一个volatile关键字
*/
//endregion
//region 静态内部类实现的单例模式 既保证了只存在一个单例,又保证了线程安全
public static class SinglePerson {
private static Person mPerson6 = new Person();
}
public static Person getmPerson6() {
return SinglePerson.mPerson6;
}
/**
* 当外部类Person被加载时,其静态内部类SinglePerson不会被加载,所以它的成员变量mPerson6是不会被初始化的,
* 只有当调用Person.getInstance6()方法时,才会加载SinglePerson并且初始化其成员变量,而类加载时是线程安全的,
* 这样既保证了延迟加载,也保证了线程安全,同时也简化了代码量,一举三得!
*/
//endregion
//region 序列化创建单例
//无亲自写过
//endregion
//region 《Effective Java》一书中,作者Joshua Bloch提倡可以采用枚举的方式 最佳方案
public static Person getmPerson7() {
return EnumSinglePerson.INSTANCE.getInstance();
}
private enum EnumSinglePerson {
INSTANCE;
private Person mPerson7;
EnumSinglePerson() {
mPerson7 = new Person();
}
public Person getInstance() {
return mPerson7;
}
}
//endregion
}
单例几种实现方式和优缺点,最佳实现方案记录
最新推荐文章于 2025-11-12 17:55:06 发布
本文详细探讨了单例模式的多种实现方式,包括饿汉式、懒汉式及其升级版,静态内部类和枚举方式。对比分析了各种实现的优缺点,如对象持久存在、多线程安全性和性能开销等问题。

277

被折叠的 条评论
为什么被折叠?



