设计模式-单例模式

单例模式
一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例
模式。
为什么要用单例模式
节省资源
处理资源冲突

饿汉式

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();
	}
}

在类加载的时候,instance 静态实例就已经创建并初始化好
了,所以,instance 实例的创建过程是线程安全的。不过,这样的实现方式不支持延迟加载(在真正用到 IdGenerator 的时候,再创建实例)

懒汉式

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();
	}
}

懒汉式相对于饿汉式的优势是支持延迟加载。

双重检测

  1. 双重检测
    饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一种既支持延迟
    加载、又支持高并发的单例实现方式,也就是双重检测实现方式。
    在这种实现方式中,只要 instance 被创建之后,即便再调用 getInstance() 函数也不会再
    进入到加锁逻辑中了。所以,这种实现方式解决了懒汉式并发度低的问题。具体的代码实现
public class IdGenerator {
	private AtomicLong id = new AtomicLong(0);
	private static 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();
	}
}

还有两种方法可以解决
静态内部类:
利用 Java 的静态内部类来实现单例。这种实现方式,既支持延迟加载,也支持高并发,实
现起来也比双重检测简单。

枚举:
最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过 Java 枚举类型本身的特
性,保证了实例创建的线程安全性和实例的唯一性。

单例存在哪些问题?

1.对OOP特性支持不太友好
OOP四大特性:封装,多态,抽象,继承

2.对代码扩展不太好,如果需要两个实例时,会对代码改动太大

3.对测试可能会有影响

4.不支持有参的构造,但是用配置文件可以解决这个问题

实现线程唯一

Thread.currentThread表示当前代码段正在被哪个线程调用的相关信息。

public class IdGenerator {
	private AtomicLong id = new AtomicLong(0);
	private static final ConcurrentHashMap<Long, IdGenerator> instances
		= new ConcurrentHashMap<>();
	private IdGenerator() {}
	public static IdGenerator getInstance() {
		Long currentThreadId = Thread.currentThread().getId();
		instances.putIfAbsent(currentThreadId, new IdGenerator());
		return instances.get(currentThreadId);
	}
	public long getId() {
		return id.incrementAndGet();
	}
}

Thread.currentThread()和This的区别

实现集群内唯一

public class IdGenerator {
	private AtomicLong id = new AtomicLong(0);
	private static IdGenerator instance;
	private static SharedObjectStorage storage = FleSharedObjectStorage(入参省略)
	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();

具体来说,需要把这个单例对象序列化并存储到外部共享存储区(比如文件)。进程在使用这个单例对象的时候,需要先从外部共享存储区中将它读取到内存,并反序列化成对象,然后再使用,使用完成之后还需要再存储回外部共享存储区。

为了保证任何时刻,在进程间都只有一份对象存在,一个进程在获取到对象之后,需要对对象加锁,避免其他进程再将其获取。在进程使用完这个对象之后,还需要显式地将对象从内存中删除,并且释放对对象的加锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值