今天,我们主要讨论的问题有,

如何理解单例模式的唯一性

如何实现线程唯一的单例

如何实现集群环境下的单例式

如何实现一个多例

1.如何理解单例模式下的唯一性

一个类只能创建一个对象,那这就是一个单例类,这个设计模式就是单例式设计模式

那么一个类只能创建一个对象,这个唯一对象的范围是什么呢?是一个线程还是一个进程呢?自然是一个进程

对于进程来说,就可以认为是一个jar的整体执行范围

也是因为进程之间是不共享内存地址的,所以不会出现进程间的全局唯一的问题,所以一般单例式的作用范围是进程内唯一的,在线程也是唯一的,因为一个进程内有多个线程

2.如果实现一个线程唯一的单例呢?

因为一个进程内有多个线程,而要做到线程唯一就只要保证在这个进程内可以重复就可以了,例如使用Java提供的ThreadLocal,简单点的实现就是利用HashMap来存储对象,key是线程ID,value是对象,那么这样就可以保证不同线程对应的不同的对象,实现的代码基本如下

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

}

}

3.如何实现集群环境下的单例呢?

集群唯一可以说,就是多个进程的集合,多个进程的唯一,不同进程间共享一个对象,不能创建一个类的多个对象,

有一种思路就是,有一个外部文件,可以让不同的进程去读取,具体点,就是我们将一个单例化的对象序列化存储到一个外部存储区中,不同进程去读取这个外部文件,将其反序列号到内存中使用,并且给这个文件加上锁,使用完成后,将对象从内存中删除,并且释放对文件的锁,实现可以如下

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

4.如何实现一个多例模式?

单例式只能创建一个对象,多例是指,一个类可以创建多个对象,但是个数是有限的,类似对象池,可以简单的实现如下

public class BackendServer {

private long serverNo;

private String serverAddress;

private static final int SERVER_COUNT = 3;

private static final Map<Long, BackendServer> serverInstances = new HashMap<>();

static {

serverInstances.put(1L, new BackendServer(1L, “192.134.22.138:8080”));

serverInstances.put(2L, new BackendServer(2L, “192.134.22.139:8080”));

serverInstances.put(3L, new BackendServer(3L, “192.134.22.140:8080”));

}

private BackendServer(long serverNo, String serverAddress) {

this.serverNo = serverNo;

this.serverAddress = serverAddress;

}

public BackendServer getInstance(long serverNo) {

return serverInstances.get(serverNo);

}

public BackendServer getRandomInstance() {

Random r = new Random();

int no = r.nextInt(SERVER_COUNT)+1;

return serverInstances.get(no);

}

}

或者说,一个类型的对象只能创建一个,不同类型的对象,可以创建多个,这个类型怎么理解

这一认为是不同的类,传入不同的类来获取到不同的实例独享

public class Logger {

private static final ConcurrentHashMap<String, Logger> instances

= new ConcurrentHashMap<>();

private Logger() {}

public static Logger getInstance(String loggerName) {

instances.putIfAbsent(loggerName, new Logger());

return instances.get(loggerName);

}

public void log() {

//…

}

}

//l1==l2, l1!=l3

Logger l1 = Logger.getInstance(“User.class”);

Logger l2 = Logger.getInstance(“User.class”);

Logger l3 = Logger.getInstance(“Order.class”);

这种多例模式的理解类似工厂模式,但是多例模式创建的都是同一个类的对象,工厂模式创建的是不同对象子类的对象

本章的重点很简单

1.如何理解单例模式的唯一性

单例模式的唯一性是一个进程唯一的,进程唯一表示的是进程内唯一,也表示线程唯一

2.如何实现你线程唯一的单例

可以利用HashMap来实现,将不同线程作为key,对象作为value,做到不同线程对应不同的对象

3.如何实现集群环境下的单例,将单例对象序列化并存储到外部共享区域,然后使用的时候,从外部区域去读到内存,反序列化为对象,并给外部共享加上锁,然后使用完成,显式的将对象从中删除,并且释放掉锁

4,如何实现一个多例模式

可以认为是一个类可以创建多个对象,但是数量有限

也可以认为利用Map来创建对象类型和对象之间的关系,控制个数

发表评论

邮箱地址不会被公开。 必填项已用*标注