kotlin 之单例类详解

Posted 易帜¤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kotlin 之单例类详解相关的知识,希望对你有一定的参考价值。

object

单例对象的声明:

object  Model
    var temp = "1"
    val temp2 = "2"
    const val temp3 = "3"

抛出疑问:使用object修饰的类,是哪种类型的单例模式

这里我们先回顾一下java六种单例模式

1. 饿汉式
public class HungryMan 

    private HungryMan()

    private static HungryMan hungryMan = new HungryMan();

    public static HungryMan getInstance()
        return hungryMan;
    

优点:简单方便,线程安全

缺点:无论是否用到,都会进行实例化,而且在类加载时就会实例化

2. 懒汉式
public class LazyMan 

    private static LazyMan lazyMan = null;

    private LazyMan() 
    

    public static LazyMan getInstatce() 
        if (lazyMan == null) 
            lazyMan = new LazyMan();
        
        return lazyMan;
    


优点:只有在使用时才会生成对象,能够减少内存开销

缺点:线程不安全,只能在单线程中使用,多个线程访问时,会产生多个对象,

3.懒汉式同步锁
public class LazyMan 

    private static volatile LazyMan lazyMan = null;

    private LazyMan() 
    

    public static LazyMan getInstatce() 
        synchronized (LazyMan.class)
            if (lazyMan == null) 
                lazyMan = new LazyMan();
            
        
        return lazyMan;
    

优点:支持多线程

缺点:每次都会有一个加锁以及释放锁的操作,效率低,可以通过反射破坏单例模式。

4.DCL双检测锁
public class LazyMan 

    private static volatile LazyMan lazyMan = null;

    private LazyMan() 
    

    public static LazyMan getInstatce() 
        if(lazyMan == null)
            synchronized (LazyMan.class)
                if (lazyMan == null) 
                    lazyMan = new LazyMan();
                
            
        
        return lazyMan;
    

这里引入一下解释一下DCL双检测锁机制:

DCL双检测锁机制: 用DCL双检测锁机制为什么要用valoatile修饰,因为lazyMan=new LazyMan(), 并非是一个原子操作。事实上在JVM中大概做了3件事。

1.给lazyMan分配内存,

2.调用构造器来初始化成员变量

3.将lazyMan对象指向分配的内存空间。 但是JVM的即时编译器中存在指令重排序的优化,也就是说上面的第二步,第三步顺序是不 确定的一旦2,3,顺序乱了,这个是有一个线程调用了方法,结果虽然是非null,但是未初 始化,所以直接报错。

优点:效率高,线程安全

缺点:代码复杂,可以通过反射破坏单例

5.静态内部类
public class Singleton 
 
    private Singleton() 
 
    private static class SingletonInstance //私有静态内部类
        private static final Singleton INSTANCE = new Singleton();
    
 
    public static Singleton getInstance() 
        return SingletonInstance.INSTANCE;
    

优点:类的静态属性只有在第一次加载类的时候初始化,所以线程安全

缺点:代码变得复杂,apk文件增大

6. 枚举单例
public enum SingleTon 

    SINGLE_TON;

    private String field;

    public String getField() 
        return field;
    

    public void setField(String field) 
        this.field = field;
    

优点:线程安全,不用担心反射破话单例模式

缺点:枚举类占用内存多

解析:object 单例类是什么类型的单例

这里我们直接将kotlin代码转为Java 代码进行查看。

kotlin代码如下

转为Java之后

我们可以看到,该Model类转为Java代码之后,它是一个饿汉式单例。所以使用object的类采用的是饿汉式单例。

companion object伴生对象出现的单例是哪种类型的单例

kotlin代码如下

class  Model
    companion object
        val text = ApiWrapper("11")
    


class ApiWrapper (val api : String)
     fun s() 
      
    

java代码如下

public final class Model 
   @NotNull
   private static final ApiWrapper text = new ApiWrapper("11");
   @NotNull
   public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);

   public static final class Companion 
      @NotNull
      public final ApiWrapper getText() 
         return Model.text;
      

      private Companion() 
      
      public Companion(DefaultConstructorMarker $constructor_marker) 
         this();
      
   


可以看的出来,如果直接对text赋值,那么就相当于是一个饿汉式加载

但是如果我们对text进行by lazy延迟赋值,那么会是什么样子呢。

public final class Model 
   @NotNull
   private static final Lazy text$delegate;
   @NotNull
   public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);

   static 
      text$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   

   public static final class Companion 
      @NotNull
      public final ApiWrapper getText() 
         Lazy var1 = Model.text$delegate;
         Model.Companion var2 = Model.Companion;
         Object var3 = null;
         return (ApiWrapper)var1.getValue();
      

      private Companion() 
      

      public Companion(DefaultConstructorMarker $constructor_marker) 
         this();
      
   

可以看出,此时变成了懒汉式同步单例

至于为什么是同步单例,这里需要大家去看一下LazyKt.lazy()方法

boost库之单例类

(七)boost库之单例类

    一、boost.serialzation的单件实现

    单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

    单例,通常在一个大型项目中单例是非常常见的,boost库没有提供专门的单例类,但可以在其它库中找到他的实现

#include <boost/serialization/singleton.hpp> 
class ThreadPoll : public boost::serialization::singleton<ThreadPoll>
{

};

 

当然,你也可以不使用继承的方式,只需要typedef一下,就可以得到一个全局访问点,这种方式会更加的灵活。

class ThreadPoll
{

};
typedef boost::serialization::singleton<ThreadPoll> ThreadPollAgent;

 

但是,该单例是非线程安全的,单件类实现了一个全局访问点,如果在多个线程环境中访问,是需要自行加锁的,因此,如果需要一个线程安全的单件,需要自己实现。

 

二、自行实现线程安全单件

/******************************************************
* 支持线程安全的单例类
*******************************************************/

#ifndef __SINGLETON_H__
#define __SINGLETON_H__

#include <boost/noncopyable.hpp>
#include <boost/thread.hpp>

//哨兵类,负责多线程操作,自动加锁解锁
//哨兵类不允许拷贝,
template<typename T>
class SingletonGuard : boost::mutex::scoped_lock, public boost::noncopyable
{
public:
    explicit SingletonGuard(T* inst, boost::mutex& mt):boost::mutex::scoped_lock(mt),m_guardPtr(inst)
    {         
    }
    T* operator->()
    {
        return m_guardPtr;
    }
private:
    T* m_guardPtr;
};

//监视类,用于监视单例的状态
template<typename T>
class Singleton_wrapper : public T
{
public:
    static bool m_is_destroyed;
    ~Singleton_wrapper(){
        m_is_destroyed = true;
    }
};

template<typename T>
bool Singleton_wrapper< T >::m_is_destroyed = false;

//单例
template<typename T>
class Singleton : public boost::noncopyable
{
public:
    static SingletonGuard<T> get_mutable_instance(){
        return SingletonGuard<T>(&get_instance(), m_signalMutex);
    }
    static const T & get_const_instance(){
        return get_instance();
    }
private:
    static T & instance;
    static void use(T const &) {}
    static T & get_instance() {
        static Singleton_wrapper< T > t;
        //编译期进行初始化实例
        BOOST_ASSERT(! Singleton_wrapper< T >::m_is_destroyed);
        use(instance);
        return static_cast<T &>(t);
    }
    static boost::mutex m_signalMutex; 
protected:
    boost::mutex::scoped_lock ScopedLock()
    {
        return boost::mutex::scoped_lock(m_signalMutex);
    }
};
template<typename T>
boost::mutex Singleton< T >::m_signalMutex; 

template<typename T>
T & Singleton< T >::instance = Singleton< T >::get_instance();

#endif //__SINGLETON_H__

 

该类参考boost单件的实现,使用上相似,但get_mutable_instance返回的不是实例对象,而是保存该实例指针的一个临时变量,通过定义->符号访问实例的函数,离开该作用域时,临时变量析构,

自行解锁。通过该单例访问的所有函数都将已加锁的形式进行访问,如果想在不加锁的情况下访问,只能通过get_const_instance获取实例的方式访问。

你可以像下面这样使用:

class ThreadPoll
{
public:
    void Update(){};
    int Query()const{};
};
typedef Singleton<ThreadPoll> ThreadPollAgent;
ThreadPollAgent::get_mutable_instance()->Update();
ThreadPollAgent::get_const_instance().Query();

以上是关于kotlin 之单例类详解的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin中数据类和单例类的实现和讲解面向对象编程接口的实现

Kotlin 初学者抽象类-嵌套类-单例类-接口

当Kotlin邂逅设计模式之单例模式

Kevin Learn Kotlin:数据类单例类枚举类

Kotlin 初学者单例类(object)

《JAVA与模式》之单例模式