缺点:但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
四、懒汉式(线程安全)
/**
- 懒汉式(线程安全,同步方法)
*/
public class King {
private static King kingInstance;
private King() {
}
public static synchronized King getKingInstance() {
if (kingInstance == null) {
kingInstance = new King();
}
return kingInstance;
}
}
-
优点:懒加载,只有使用的时候才会加载,获取单例方法加了同步锁,保障线程安全。
-
缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。
五、懒汉式(线程安全,同步代码块)
/**
- 懒汉式(线程安全,同步代码块)
*/
public class King {
private static King kingInstance;
private King() {
}
public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
kingInstance = new King();
}
}
return kingInstance;
}
}
-
优点:改进了第四种效率低的问题。
-
缺点:不能完全保证单例,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
六、双重检查(DCL)
/**
- 双重检查(DCL)
*/
public class King {
private static volatile King kingInstance;
private King() {
}
public static King getKingInstance() {
if (kingInstance == null) {
synchronized (King.class) {
if (kingInstance == null){
kingInstance = new King();
}
}
}
return kingInstance;
}
}
-
优点:线程安全;延迟加载;效率较高。
-
缺点:JDK < 1.5 的时候不可用
-
不可用原因:由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,而JDK 1.5 以及之后的版本都修复了这个问题。(面试装逼用,谨记!!!)
七、静态内部类
/**
- 静态内部类
*/
public class King {
private King() {
}
private static class KingInstance{
private static final King KINGINSTANCE = new King();
}
public static King getInstance(){
return KingInstance.KINGINSTANCE;
}
}
-
优点:避免了线程不安全,延迟加载,效率高。
-
缺点:暂无,最推荐使用。
-
特点:这种方式跟饿汉式方式采用的机制类似,但又有不同。
-
两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
八、枚举
/**
-
枚举
*/
public enum King {
KINGINSTANCE;
} -
优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
-
缺点:JDK 1.5之后才能使用。
九、容器类管理
/**
- 使用容器实现单例模式(可以用于管理单例,有兴趣的可以尝试一下)
- */
class InstanceManager {
private static Map<String, Object> objectMap = new HashMap<>();
private InstanceManager(){}
public static void registerService(String key,Object instance){
if (!objectMap.containsKey(key)){
objectMap.put(key,instance);
}
}
public static Object getService(String key){
return objectMap.get(key);
}
}
/**
- 使用方式
- Dog类就不贴出来了
- 自己随便写个就行
- 可以运行一下看看 打印的地址是否一致
*/
class Test {
public static void main(String[] args) {
InstanceManager .registerService(“dog”, new Dog());
Dog dog = (Dog) InstanceManager .getService(“dog”);
Dog dog2 = (Dog) InstanceManager .getService(“dog”);
Dog dog3 = (Dog) InstanceManager .getService(“dog”);
Dog dog4 = (Dog) InstanceManager .getService(“dog”);
System.out.println(dog);
System.out.println(dog2);
System.out.println(dog3);
System.out.println(dog4);
}
}
-
优点:在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作, 降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
-
缺点:不常用,有些麻烦
九种写法的优劣对比;
①:不可用原因:由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,而JDK 1.5 以及之后的版本都修复了这个问题。(面试装逼用,谨记!!!)
②:这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用, 而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
单例模式在Android源码中的应用
在我们每天接触的Android源码中其实也有很多地方用到了单例模式:
1、EventBus中获取实例:
private static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,EventBus采用的是双重检查(DCL)的方式实现的单例模式。
2、InputMethodManager获取实例
static InputMethodManager sInstance;
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
}
}
我们看到,其实这里是懒汉式(同步代码块)方式的改写,去掉了外部判断为空,放到了里面。然后通过ServiceManger.getService()方法,通过容器的方式获取了单例。
在Android很多系统服务都是通过容器获取的单例。
单例模式在日常开发中的应用场景
日常开发中我们也有些场景是需要用到单例模式的,例如:
1、图片加载
2、网络请求
3、工具类封装
案例有很多,相信大家也都有用到,我就不列举了。这里我们如何合理的在项目中使用单例模式。
合理的辨析一个设计是否应该为单例模式前,大家先问问自己几个问题,也是检验标准:
Quote from 《 Use your singletons wisely 》
Will every application use this class exactly the same way? (keyword: exactly)
Will every application ever need only one instance of this class? (keyword: ever & one)
Should the clients of this class be unaware of the application they are part of?每一个应用(组件/模块)是否以完全一致的方式来使用这个类?
每一个应用(组件/模块)是否真的只需要这个类的一个实例呢?
对于这个类的客户端类来说,对他们自己是应用中的一部分这件事是否应该保持毫无察觉的状态呢?
以上3条就是检验一个类是否应该被设计为单例模式的判断准则,
如果我们对于以上这3条均给出了“是的”的答案,那么这个类就是可以被设计为单例模式了。反之还是不要用的好。
单例模式的优点
单例模式的优点其实已经在定义中提现了:可以减少系统内存开支,减少系统性能开销,避免对资源的多重占用、同时操作。
单例模式的缺点
任何事物都不是完美的,单例模式也是如此,它也存在以下几个缺点:
1、违反了单一责任链原则,测试困难
单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
2、扩展困难
由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。修改功能必须修改源码。
3、共享资源有可能不一致。
现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的共享对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致共享的单例对象状态的丢失。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。
技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;
我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
4)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!