今天的文章很短,但是很经典,值得你仔细阅读每一个文字…
正如我开篇所说,我们要整理一些java并发编程的学习文档,这一篇就是第八篇:单例模式。这一篇主要聊聊单例的几种实现方式已经适用的场景。
专业解释:单例就是确保一个类只有一个实例,并且有一个全局获取这个实例的访问点。
简单的说呢就是一个类只能创建一个实例,任何时候使用这个类的实例对象都是同一个。
基本都是了减少这个类对象创建和销毁过程的消耗。
嗯!
思考思考,一个类如果只有一个实例,必然不能随便创建,所以单例类的关键代码就是构造方法是私有的,不允许在其他地方随便创建。
单例的优点非常明确:因为只有一个实例,所以减少了内存的开销,尤其是创建和销毁的时候减少了资源浪费。避免了对资源的多重占用。
单例的缺点呢:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
单例有7种实现方式
直接上菜:
/**
* @author 戴着假发的程序员
*/
public class Singleton {
// 构造方法私有化
private Singleton(){}
// 私有静态当前类对象作为成员变量
private static Singleton instance;
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
上面的程序非常简单:如果要获取Singleton类的实例对象可以这样写Singleton.getInstance()
你会发现:
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);// true 额。。。。。多执行几次,有可能也会是false
说明:
这个方式一开始并没有创建instance,在第一次使用的时候才会创建,这才是懒汉模式的真谛。有效的实现了延迟加载。节省了内存。
这个方式不是线程安全的,可能会有隐患。
如果线程A调用getInstance方法,判断了instance为null,正准备创建对象,结果CPU不给资源了,于是就稍微停了一会,这是线程B也调用了getInstance方法,结果判断instance依然是null,于是就创建了instance对象。之后线程A开始执行,线程A不会再判断了,直接创建instance对象,这样的话这个类的对象就被创建了两次。
上菜:
/**
* @author 戴着假发的程序员
*/
public class Singleton1 {
// 构造方法私有化
private Singleton1(){}
// 私有静态当前类对象作为成员变量
private static Singleton1 instance;
// 静态的公共的可以获取当前类实例对象的方法
public static synchronized Singleton1 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
这个测试吗很简单:
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);// true 额。。。这里无论执行多少次都是true
说明:
饿汉模式关键在这个“饿”字。额… 记得以前我吃饭吃的很着急的时候,我亲爱的老母亲就说:“慢点吃,搞得饿死鬼上身似得”。非常庆幸的是老母亲现在也会这样说我。哈哈哈哈哈。。。。
言归正传:饿汉模式就是在一开始的时候直接创建实例对象
/**
* @author 戴着假发的程序员
*/
public class Singleton2 {
// 构造方法私有化
private Singleton2(){}
// 私有静态当前类对象作为成员变量
private static Singleton2 instance = new Singleton2();
// 静态的公共的可以获取当前类实例对象的方法
public static synchronized Singleton2 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
饿汉模式真的有点饿,一开始就创建了。
但是如果,现在不饿呢,直接把饭煮了是不是会有点浪费。
所以饿汉模式的问题就是如果一开始就把类对象创建好了, 但是这个对象长时间用不到,那么就是有些浪费资源了。
所以呀,还是要看实际的使用场景的。
程序,必须追求效率和省资源。所以我们希望单利模式中创建对象的方法也是效率高高滴。
但是我们一旦给方法加锁必然降低方法的执行效率,所以双重校验锁可能是一个不错的选择,既能能相对的提高程序效率,又能保证线程安全。
具体的实现就是:
/**
* @author 戴着假发的程序员
*/
public class Singleton3 {
// 构造方法私有化
private Singleton3(){}
// 私有静态当前类对象作为成员变量
private static Singleton3 instance;
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton3 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if (instance==null) {
// 上锁
synchronized (Singleton3.class) {
// 再次判断instance是否存在,如果存在就直接返回,如果不存在就创建
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
静态内部类的实现方式也叫登记式。是个啥情况呢:
直接上菜:
/**
* @author 戴着假发的程序员
*/
public class Singleton4 {
// 构造方法私有化
private Singleton4(){}
// 准备一个静态内部类
private static class SingletonHolder{
// 申明并且实例化一个外部单利类的实例对象,并且设置为常量。
private final static Singleton4 INSTANCE = new Singleton4();
}
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton4 getInstance(){
// 直接返回内部类的成员常量
return SingletonHolder.INSTANCE;
}
}
这种方式有啥用?
这个方式实在太简单,也好理解(当然你首先要知道啥是枚举)。所以就不多做解释了。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
关于单利模式的情况就是这样了。
还有其他的并发编程相关的内容,我会持续更新,欢迎关注。
我是”起点编程“的"戴着假发的程序员" 欢迎关注…欢迎评论。。。。。
起点编程-是你我的未来…
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/q2780004063/article/details/125575141
内容来源于网络,如有侵权,请联系作者删除!