-
Notifications
You must be signed in to change notification settings - Fork 784
2019-03-25:请简要谈一谈单例模式? #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
单例模式,主要用于某些环境下对象的唯一性,分为线程安全和不安全,写法也比较多,但是个人认为只要熟悉主要的几种就行,包括双检查、静态内部类、枚举 |
楼上说的很对,对象重复创建消耗资源很大,单例也仅仅是避免对象重复创建,节省内存,避免多个实例存在线程的不安全,存在多个实例对象,业务逻辑就会复杂很多,至于写法就看业务跟个人喜好了,写法很多,楼上已经说了 |
单例分为懒汉模式和恶汉模式
在代码 在多线程中 两个线程可能同时进入代码2, synchronize保证只有一个线程能进入下面的代码, |
借助类加载机制,可以在不使用synchronized等内容的情况下,最高效的实现单例。 public class Singleton {
public static Singleton getInstance(){
// 1. 调用该方法时,才会访问 LazyHolder.INSTANCE这个静态类的静态变量
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
// 2. 访问 LazyHolder.INSTANCE才会触发Singleton的初始化
static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
}
|
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例 。
public class Evilman{
//在自己内部定义自己的一个实例,只供内部调用 不判空直接怼
private static final Evilman mEvilman= new Evilman();
private Evilman(){
}
//直接访问
public static EvilmangetInstance(){
return mEvilman;
}
}
private static Slacker mSlacker= null;
private Slacker(){}
public static synchronized Slacker getslacker() {
// 为空就new
if (mSlacker== null)
mSlacker= new Slacker();
return mSlacker;
}
private static Slacker mSlacker= null;
private Slacker(){}
public static Slacker getslacker() {
// 为空就new
if(mSlacker== null){
synchronized (Slacker .class) {
if (mSlacker== null){
return mSlacker= new Slacker();
}
}
}
return mSlacker;
} |
这种也行吗?有没有优缺点,哪位同学呢个讲一下吗 |
可以参考一下《Android源码设计模式解析与实战》,里面对于单例的实现方式做了比较详细的讲解。:smile: |
顺便问一下,无论是单例、多实例、还是静态方法,我们大部分都是在访问”方法“。那么单例和静态方法有什么区别?用单例,我干嘛不把单例里面的方法都定义为静态的,成员变量也定义为静态的? |
单例模式,主要用于某些环境下对象的唯一性,分为线程安全和不安全。 |
|
同样的,定义一个类里面全部用静态方法和静态属性同样可以实现... 如果太局限于“标准答案”,面试官稍微延伸一点就会答不上来。。。 |
其实,了解下单例模式的历史,以及每种单例模式要解决的核心问题,那么这个问题就能串到一起了 来来,继续回答问题三段式
|
对了,Android系统中还有一种单例模式,叫啥来着,HashMap实现的单例 public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager() {
}
public static void putObject(String key, String instance){
if(!objMap.containsKey(key)){
objMap.put(key, instance);
}
}
public static Object getObject(String key){
return objMap.get(key);
}
}
```
别介意,纯手打 |
有些实例需要在整个环境中以单例的形式存在,或者多次创建需要耗费很大资源。所以需要设定单例模式来保证他的唯一性。
|
synchronized 能保证每次只有一个线程访问代码块,即 instance 实例创建的原子性,volatile 保证了 instance 对多个线程的内存可见性。 |
其实吹毛求疵来说,这种是使用了synchronized,详见ClassLoader 类加载机制,loadClass有使用到synchronized。如果非要说不使用synchronized的话,可以参考下CAS,不过客户端开发我觉得基本没必要。 |
https://blog.csdn.net/mnb65482/article/details/80458571 |
世人都只知道应用级别的单例,如果你能说出线程级别的单例,会加分哦! |
单列模式
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
这种方式和上面的方式一样,只不过将类实例化的过程放入了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例,优缺点一样。
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()方法进行了线程同步。缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。
由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的的代码块。但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。优点:线程安全;延迟加载;效率较高。
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。优点:避免了线程不安全,延迟加载,效率高。
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。优点:系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。缺点:当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。适用场合:需要频繁的进行创建和销毁的对象;创建对象时耗时过多或耗费资源过多,但又经常用到的对象;工具类对象;频繁访问数据库或文件的对象 |
优点: 缺点: |
HashMap的函数是非同步的,不是线程安全的,这样没问题吗? |
什么是单例? 设计单例主要考虑到确保一个实例,线程安全,提高效率,代码简洁几方面。 实现方式?
双重校验锁方式如果提到 volatile 可以问下 volatile 变量?
你推荐哪种方式?为什么? 还能想到单例的其他问题吗?
|
The text was updated successfully, but these errors were encountered: