速发国际365的最新网站-百特365平台可靠吗-日博365投注网

单例模式学习笔记

设计模式之美学习笔记 什么是单例模式? 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它

单例模式学习笔记

设计模式之美学习笔记

什么是单例模式?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象,也就是说不能使用new关键字来创建对象。

为什么使用单例模式

public class Logger {

private FileWriter writer;

public Logger() {

File file = new File("/Users/shadowzlh/log.txt");

writer = new FileWriter(file, true); //true表示追加写入

}

public void log(String message) {

writer.write(message);

}

}

// Logger类的应用示例:

public class UserController {

private Logger logger = new Logger();

public void login(String username, String password) {

// ...省略业务逻辑代码...

logger.log(username + " logined!");

}

}

public class OrderController {

private Logger logger = new Logger();

public void create(OrderVo order) {

// ...省略业务逻辑代码...

logger.log("Created an order: " + order.toString());

}

}

在这个例子中, 当两个线程进行都进行日志写入的时候,会导致日志覆盖的情况

单例模式的使用场景

需要生成唯一序列的环境

需要频繁实例化然后销毁的对象。

创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

方便资源相互通信的环境

单例模式的分类

饿汉式

在类加载的时候,instance 静态实例就已经创建并初始化好了,所以,instance 实例的创建过程是线程安全的。

懒汉式

当需要使用的时候才对对象进行创建,多线程环境下,每个线程抢占Singleton类的对象资源,但是可能会发生对个线程同时请求对象实例的问题,这个时候就有可能创建多个对象,从而导致数据不一致,就会出现线程安全问题。

单例模式实现方式

饿汉式实现方式

public class IdGenerator {

private AtomicLong id = new AtomicLong(0);

private static final IdGenerator instance = new IdGenerator();

private IdGenerator() {}

public static IdGenerator getInstance() {

return instance;

}

public long getId() {

return id.incrementAndGet();

}

}

懒汉式实现方式

public class IdGenerator {

private AtomicLong id = new AtomicLong(0);

private static IdGenerator instance;

private IdGenerator() {}

public static synchronized IdGenerator getInstance() {

if (instance == null) {

instance = new IdGenerator();

}

return instance;

}

public long getId() {

return id.incrementAndGet();

}

}

我们给 getInstance() 这个方法加了一把大锁(synchronzed),导致这个函数的并发度很低。量化一下的话,并发度是 1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈,这种实现方式就不可取了。

双重检查锁

public class IdGenerator {

private AtomicLong id = new AtomicLong(0);

private static volatile IdGenerator instance;

private IdGenerator() {}

public static IdGenerator getInstance() {

if (instance == null) {

synchronized(IdGenerator.class) { // 此处为类级别的锁

if (instance == null) {

instance = new IdGenerator();

}

}

}

return instance;

}

public long getId() {

return id.incrementAndGet();

}

}

为什么使用双重检查锁

假设存在两个线程 a , b

a,b 调用 getInstance 方法 , 都经过了第一个的检查,此时a获得锁进行实例化,a 实例化之后,释放锁,此时b获得锁进行实例化,没有第二次判断的情况下,b 线程会将instance 重复实例化

volatile 的作用:

CPU 指令重排序可能导致在 IdGenerator 类的对象被关键字 new 创建并赋值给 instance 之后,还没来得及初始化(执行构造函数中的代码逻辑),就被另一个线程使用了。这样,另一个线程就使用了一个没有完整初始化的 IdGenerator 类的对象。要解决这个问题,我们只需要给 instance 成员变量添加 volatile 关键字来禁止指令重排序即可。

静态内部类

public class IdGenerator {

private AtomicLong id = new AtomicLong(0);

private IdGenerator() {}

private static class SingletonHolder{

private static final IdGenerator instance = new IdGenerator();

}

public static IdGenerator getInstance() {

return SingletonHolder.instance;

}

public long getId() {

return id.incrementAndGet();

}

}

使用 jvm 本身自己的机制实现单例模式

枚举

public enum IdGenerator {

INSTANCE;

private AtomicLong id = new AtomicLong(0);

public long getId() {

return id.incrementAndGet();

}

}

集群间唯一的单例

需要把这个单例对象序列化并存储到外部共享存储区(比如文件)。进程在使用这个单例对象的时候,需要先从外部共享存储区中将它读取到内存,并反序列化成对象,然后再使用,使用完成之后还需要再存储回外部共享存储区。为了保证任何时刻,在进程间都只有一份对象存在,一个进程在获取到对象之后,需要对对象加锁,避免其他进程再将其获取。在进程使用完这个对象之后,还需要显式地将对象从内存中删除,并且释放对对象的加锁。

public class IdGenerator {

private AtomicLong id = new AtomicLong(0);

private static IdGenerator instance;

private static SharedObjectStorage storage = FileSharedObjectStorage(/*入参省略,比如文件地址*/);

private static DistributedLock lock = new DistributedLock();

private IdGenerator() {}

public synchronized static IdGenerator getInstance()

if (instance == null) {

lock.lock();

instance = storage.load(IdGenerator.class);

}

return instance;

}

public synchroinzed void freeInstance() {

storage.save(this, IdGeneator.class);

instance = null; //释放对象

lock.unlock();

}

public long getId() {

return id.incrementAndGet();

}

}

// IdGenerator使用举例

IdGenerator idGeneator = IdGenerator.getInstance();

long id = idGenerator.getId();

idGenerator.freeInstance();

单例模式的替代方案

为了保证全局唯一,除了使用单例,我们还可以用静态方法来实现。不过,静态方法这种实现思路,并不能解决我们之前提到的问题。如果要完全解决这些问题,我们可能要从根上,寻找其他方式来实现全局唯一类了。比如,通过工厂模式、IOC 容器(比如 Spring IOC 容器)来保证,由程序员自己来保证(自己在编写代码的时候自己保证不要创建两个类对象)。

← 上一篇: DNF手游深渊碎片多久能攒1000?
下一篇: 韓國電影 →

相关推荐

液晶电视和led电视的区别在哪里

液晶电视和led电视的区别在哪里

电视从黑白到彩色后,运用在 电视机 上面的技术就越来越多了,导致目前人们见到的电视产品也丰富多样。目前在电视机市场上卖得比较好的

打嗝挂什么科

打嗝挂什么科

有来简介 | 服务条款 | 招贤纳士 | 联系我们 | 意见反馈 | 网站地图特别声明:本站内容仅供参考,不作为诊断及医疗依据。 广播电视节目制作经

联通流量日租卡哪里办(联通流量型日租卡)

联通流量日租卡哪里办(联通流量型日租卡)

在流量囧途网,我们会为您提供最全面的联通流量日租卡哪里办信息,包括联通流量型日租卡的相关知识和技巧。如果您想了解更多,请继续阅

关于春天的词语四字词语(共60句)

关于春天的词语四字词语(共60句)

1、春暖花开:形容春天温暖,花儿盛开。 2、春光明媚:形容春天阳光明媚,景色宜人。 3、凋零残花:形容春天花朵凋谢残败。 4、春意盎然:

网络用语塘主是什么意思,和海王有什么关系

网络用语塘主是什么意思,和海王有什么关系

狗姐 狗姐,是一种cp类型,指的是一方为小狼狗/小奶狗/大狗狗之类的忠犬设定,另一方就是姐姐一样的人设,可温柔,可美艳,可冷酷,也可

9、阴阳师怎么找回以前的账号

9、阴阳师怎么找回以前的账号

阴阳师怎么查询自己在哪个区首先打开手机阴阳师手游APP软件,然后在进入游戏页面点击右上角的“用户中心”按键。此时会进入到“网易用户