Java设计模式单例模式(Singleton)用法解析

网友投稿 265 2022-12-21


Java设计模式单例模式(Singleton)用法解析

这篇文章主要介绍了java设计模式单例模式(Singleton)用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

单例模式的应用场景:

单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例。并提供一个全局反访问点。单例模式是创建型模式。单例模式在生活中应用也很广泛,比如公司CEO只有一个,部门经理只有一个等。JAVA中ServletCOntext,ServetContextCOnfig等,还有spring中ApplicationContext应用上下文对象,SessionFactory,数据库连接池对象等。使用单例模式可以将其常驻于内存,可以节约更多资源。

写法:

1:懒汉模式(线程不安全)

/**

* 线程不安全的懒汉式单利模式

*

* Created by gan on 2019/11/17 17:33.

*/

public class LazySingleton {

private static LazySingleton instance;

//构造方法私有化

private LazySingleton() {

}

public static LazySingleton getInstance() {

if (instance != null) {

instance = new LazySingleton();

}

return instance;

}

}

上面的代码,提供一个静态对象instance,构造函数私有化防止外部创建对象,提供一个静态的getInstance方法来给访问者一个单例对象。这种写法的缺点就是没有考虑到线程安全问题,当多个访问者同时访问的时候很有可能创建多个对象。之所以叫懒汉式,是因为这种写法是使用的时候才创建,起到了懒加载Lazy loading的作用,实际开发中不建议采用这种写法。

2:线程安全的懒汉式(加锁)

/**

* 线程安全的懒汉式单利模式

*

* Created by gan on 2019/11/17 17:33.

*/

public class LazySingleton {

private static LazySingleton instance;

//构oWeJcuN造方法私有化

private LazySingleton() {

}

public synchronized static LazySingleton getInstance() {

if (instance != null) {

instance = new LazySingleton();

}

return instance;

}

}

这种写http://法就是在第一种的基础上添加了synchronized关键字保证了线程安全。这种写法在并发高的时候虽然保证了线程安全,但是效率很低,高并发的时候所有访问的线程都要排队等待,所以实际开发中也不建议采用。

3:恶汉式(线程安全)

/**

* 饿汉式(线程安全)

* Created by gan on 2019/10/28 22:52.

*/

public class HungrySigleton {

public static final HungrySigleton instance = new HungrySigleton();

private HungrySigleton(){}

public static HungrySigleton getInstance(){

return instance;

}

}

直接在运行(加载)这个类的时候创建了对象,之后直接访问。显然这种方式没有起到Lazy loading的效果。但是是线程安全的,实际开发中还是比较常用。

4:静态内部类(线程安全)

/**

* 静态内部类方式

* Created by gan on 2019/11/17 17:46.

*/

publichttp:// class StaticInnerClassSingleton {

//构造方法私有化

private StaticInnerClassSingleton() {}

//内部类

private static class HolderInnerClass {

//需要提供单利对象的外部类作为静态属性加载的时候就初始化

private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton();

}

//对外暴漏访问点

public static StaticInnerClassSingleton getInstance() {

return HolderInnerClass.instance;

}

}

这种内部类跟饿汉式单例有很多相似的地方,相比饿汉式单例模式的区别也是好处在于:静态内部类不在单例类加载时就加载,而是在调用getInstance()方法的时候才进行加载,达到了类似于懒汉式的效果,而且这种方法又是线程安oWeJcuN全的。实际开发中也建议采用。

5:枚举方法单例(线程安全)

/**

* 枚举单利模式

* Created by gan on 2019/11/17 17:57.

*/

public enum EnumSingleton {

INSTANCE;

public void otherMetthod() {

System.out.println("需要单利对象调用的方法。。。");

}

}

1:自由串行化。

2:保证了一个实例

3:线程安全

这种方式防止了单例模式被破坏,而且简洁写法简单,而且绝对的线程安全,但是有个缺点就是不能继承。

6:双重检查法(通常线程安全,低概率不安全)

/**

* Double check

* Created by gan on 2019/11/17 18:03.

*/

public class DoubleCheckSingleton {

private static DoubleCheckSingleton instance;

private DoubleCheckSingleton() {}

public static DoubleCheckSingleton getInstance() {http://

if (instance == null) {

synchronized (DoubleCheckSingleton.class) {

if (instance == null) {

instance = new DoubleCheckSingleton();

}

}

}

return instance;

}

}

上面的这种写法在并发极高的时候也可能会出现问题(当然这种概率非常小,但是毕竟还是有的嘛),解决的方案就是给instance的声明加上volatile关键字即可。于是就出现了下面第7总写法。

7:Double check(volatile)

/**

* Double check volatile

* Created by gan on 2019/11/17 18:03.

*/

public class DoubleCheckSingleton {

private volatile static DoubleCheckSingleton instance;

private DoubleCheckSingleton() {}

public static DoubleCheckSingleton getInstance() {

if (instance == null) {

synchronized (DoubleCheckSingleton.class) {

if (instance == null) {

instance = new DoubleCheckSingleton();

}

}

}

return instance;

}

}

volatile关键字的其中一个作用就是禁止指令重排序,把instance声明volatile后,对它的操作就会有一个内存屏障(什么是内存屏障?),这样在赋值完成之前,就不会调用读操作。这里具体的原因网上也是众说纷纭,这里不进行具体阐述。

8:ThreadLocal实现单例模式(线程安全)

/**

* ThreadLocal实现单利模式

* Created by gan on 2019/11/17 18:17.

*/

public class ThreadLocalSingleton {

private static final ThreadLocal threadLocal = new ThreadLocal() {

@Override

protected ThreadLocalSingleton initialValue() {

return new ThreadLocalSingleton();

}

};

private ThreadLocalSingleton(){}

public static ThreadLocalSingleton getInstance(){

return threadLocal.get();

}

}

ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程堆数据的访问冲突。对于多线程资源共享问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal则采用了“以空间换时间”的方式(主要就是避免了加锁排队)。 前者提供一份变量,让不同的线程排队访问,而后者为每一个线程提供了一份变量,因此可以同时访问而互不影响。但是实际是创建了多个单例对象的。

单例模式的破坏:

1:序列化破坏

一个对象创建好以后,有时候需要将对象序列化然后写入磁盘。下次在从磁盘中读取并反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即创建型的对象。这样就违背了单例模式的初衷。解决这种方式的方法就是在单例类中新增一个 private Object readResolve();方法即可,具体原因可以看看序列化和反序列化的源码。

2:反射

通过反射“暴力破解”也能破坏单例模式,具体暂时不阐述。

3:克隆

克隆也会破坏单例模式,具体暂时不阐述。

代码链接:https://gitee.com/ganganbobo/gps-parent


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Kotlin中的抽象类实现
下一篇:springboot下ueditor上传功能的实现及遇到的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~