作者:wuzhuoyu
转载地址:https://juejin.cn/post/7086852848515809311
前言
单例模式介绍
在23种设计模式之中它是最简单的,应用范围最广的一种,保证一个类仅有一个实例,并提供一个访问它的全局访问点
简介
定义
单例模式( Singleton)隶属于创建型,原理主要是一个类有且仅有一个实例,内部提供给外部该实例的访问方式。
特点
有且仅有一个实例,并且构造函数私有化,仅向外部提供获取实例的方式,一般为getInstance
概要
全局一个对象,对象构造私有化
理解实现
现实模型
大家都知道我有一个卖果子的老乡,我的果子都是在他这儿买的,老乡现在生意是越做越大了,而我还是一如既往的贫困潦倒,现在的老乡已经是我们这个社区里唯一一个卖果子的男人了,所有的居民都买他的果子,我也是其中一员,买完回家吃了写水文。
分析模型
主线:全社区居民都在老乡这买果子,我买了吃完写水文。
模型1: 社区居民 -> 消费者
模型2: 老乡 -> 单例
社区居民(消费者)
public class Resident {
public static void main(String[] args) {
List<String> allResident = null;
allResident.add("Resident1");
allResident.add("Resident2");
allResident.add("Resident3");
allResident.add("Resident4");
for (String resident : allResident) {
switch (resident){
case "Resident1":FruitStore.getInstance();
case "Resident2":FruitStore.getInstance();
case "Resident3":FruitStore.getInstance();
case "Resident4":FruitStore.getInstance();
}
}
}
}
老乡 (单例)
// 常见的双重校验写法
public class FruitStore{
private static volatile FruitStore INSTANCE;
private FruitStore(){
//私有构造函数
}
public static FruitStore getInstance(){
if(INSTANCE == null){
synchronized(FruitStore.class){
if(INSTANCE == null){
INSTSANCE = new FruitStore();
}
}
}
return INSTANCE;
}
}
使用实例
下面以activity管理类功能来理解一下,一般情况下我们的activity管理是一个频繁调用的统一资源类,在应用中也应该是唯一的存在。这种时候就可以使用单例模式了。
public class ActivityManager{
private List<Activity> activities = new ArrayList<Activity>();
private static volatile ActivityManager INSTANCE;
private ActivityManager(){
//私有构造函数
}
public static ActivityManager getInstance(){
if(INSTANCE == null){
synchronized(ActivityManager.class){
if(INSTANCE == null){
INSTSANCE = new ActivityManager();
}
}
}
return INSTANCE;
}
public void addActivity(Activity activity) {
activities.add(activity);
}
// .....
}
kotlin写法
基础写法
对应java的饿汉式写法,在类创建的同时进行对象的创建,达到了线程安全的目的,但是如果构造的耗时较长的话,会影响类的加载时间。如果只进行类的装载,没有调用便会造成资源浪费。
object ShuaiGe{}
懒加载写法
对应java的懒汉式写法,在类的实例第一次被使用时创建,解决了资源浪费问题。但是多线程并发下会出现两个对象指向同一个instance,没有达到单例效果。
public class ShuaiGe{
companion object{
val instance by lazy(LazyThreadSafetyMode.NONE){
ShuaiGe()
}
}
}
同步锁写法
对应Java的线程安全的懒汉式写法,通过Synchronized注解进行获取方法的同步限制,达到了线程安全的目的,代价就是所有的访问都成了同步。
class ShuaiGe{
companion object{
private var instance:ShuaiGe?=null
@Synchronized
fun get():ShuaiGe{
if(null==instance) instance=ShuaiGe()
return instance
}
}
}
双重校验写法
对应Java的双重DCL校验写法,通过先判断实例,在加同步锁,再判断实例,最后创建的方式实现了,达到了线程相对安全的目的,但是可能在遇到JVM指令重排序时会出现半个对象。
class ShuaiGe{
companion object{
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED){
ShuaiGe()
}
}
}
静态内部类
JVM在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类(Helper)的属性/方法被调用时才会被加载, 并初始化其静态属性(instance)。是一种推荐的单例写法,没有加锁, 线程安全, 用到时再加载, 并发行能高。
class ShuaiGe private constructor(){
companion object{
fun getInstance = Helper.instance
}
private object Helper{
val instance = ShuaiGe()
}
}
适用环境
- 应用中某个实例对象需要频繁的被访问。
- 应用中每次启动只会存在一个实例。
总结
不管以哪种形式实现单例模式,它们的核心原理是将构造函数私有化,并且通过静态公有方法获取一个唯一的实例,在这个获取的过程中必须保证线程的安全,同时也要防止反序列化导致重新生成实例对象。
网友评论