单例模式(下)

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

单例模式创建的对象是进程唯一的。
单例类在老进程中存在且只能存在一个对象,在新进程中也会存在且只能存在一个对 象。而且,这两个对象并不是同一个对象,这也就说,单例类中对象的唯一性的作用范围是 进程内的,在进程间是不唯一的。

如何实现一个线 程唯一的单例呢?

“进程唯一”指的是进程内唯一,进程间不唯一。
“线程唯一”指的是线程内唯 一,线程间可以不唯一。

我 们通过一个 HashMap 来存储对象,其中 key 是线程 ID,value 是对象。这样我们就可以做到,不同的线程对应不同的对象,同一个线程只能对应一个对象。Java 语言本 身提供了 ThreadLocal 工具类,可以更加轻松地实现线程唯一单例。
ThreadLocal 底层实现原理也是基于下面代码中所示的 HashMap。

public class IdGenerator { 
  private AtomicLong id = new AtomicLong(0); 
  private static final ConcurrentHashMap 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(); 
  } 
}

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

“集群唯一”就相当于是进程内唯一、进程间也唯一。也就是说,不同的进程间 共享同一个对象,不能创建同一个类的多个对象。

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

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

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

如何实现一个多例模式?

“多例”指的就是,一个类可以创建 多个对象,但是个数是有限制的,比如只能创建 3 个对象。

public class BackendServer { 
  private long serverNo; 
  private String serverAddress; 
  private static final int SERVER_COUNT = 3; 
  private static final Map 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 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");

这种多例模式的理解方式有点类似工厂模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值