如何实现一个单例

  1. 构造函数需要是 private 访问权限,这样才能避免外部通过 new 创建实例;
  2. 考虑对象创建时的线程安全问题;
  3. 考虑是否支持延迟加载
  4. 考虑 getInstance() 性能是否高(是否回锁)。
  5. 反序列化(可选)

单例写法

0. 懒汉式

  1. public class Singleton {
  2. private static Singleton = new Singleton();
  3. private Singleton() {
  4. }
  5. }

1. 双重检查优化版(线程安全**

  • 优点: 使用的时候才会创建对象,不会造成各种资源 的浪费,线程安全。
  • 缺点: 最优方案。

    1. public class Singleton {
    2. private static volatile Singleton instance;
    3. private Singleton() {
    4. }
    5. public static Singleton getInstance() {
    6. if (instance == null) {
    7. synchronized (Singleton.class) {
    8. if (instance == null) {
    9. instance = new Singleton();
    10. }
    11. }
    12. }
    13. return instance;
    14. }
    15. }

2. 枚举单例 (推荐*

  • 优点: 使用的时候才会创建对象,不会造成各种资源 的浪费,线程安全。能防反序列化。(effect java)
  • 缺点: 最优方案。

    1. public class Singleton {
    2. private Singleton() {
    3. }
    4. private enum SingletonEnum {
    5. SINGLETON;
    6. private Singleton singleton;
    7. SingletonEnum() {
    8. singleton = new Singleton();
    9. }
    10. private Singleton getInstance() {
    11. return singleton;
    12. }
    13. }
    14. public Singleton getInstance() {
    15. return SingletonEnum.SINGLETON.getInstance();
    16. }
    17. }

3. 静态内部类方式(*

  • 优点:使用到的时候才会创建对象,不会造成各种资源浪费问题,线程安全。
  • 缺点:没有太大缺点。
  1. public class Singleton {
  2. private Singleton() {
  3. }
  4. private static class SingletonHolder {
  5. private static final Singleton INSTANCE = new Singleton();
  6. }
  7. private static Singleton getInstance() {
  8. return SingletonHolder.INSTANCE;
  9. }
  10. }

4. 懒汉式改造(线程安全 **)

  • 优点:使用到的时候才会创建对象,不会造成各种资源浪费问题。
  • 缺点:没有线程安全问题,但是有很大的性能问题,当多个线程同时到达getInstance()方法时,需要排队进入。

    1. public class Singleton {
    2. private static Singleton instance;
    3. private Singleton() {
    4. }
    5. public synchronized static Singleton getInstance() {
    6. if (instance == null) {
    7. instance = new Singleton();
    8. }
    9. return instance;
    10. }
    11. }

为什么要使用单例 ?

一、处理资源访问冲突

  1. public class Logger {
  2. private FileWriter writer;
  3. public Logger(){
  4. File file = new File("/Users/cxw/log.txt");
  5. writer = new FileWriter(file, true);//true表示追加写入
  6. }
  7. public void log(String message) {
  8. writer.write(message);
  9. }
  10. }
  11. public class UserController {
  12. private Logger logger = new Logger();
  13. public void login(String username,String password) {
  14. //
  15. logger.log(username + " logined!");
  16. }
  17. }
  18. public class OrderController {
  19. private Logger logger = new Logger();
  20. public void create(OrderVo order) {
  21. //
  22. logger.log("Created an order: " + order.toString());
  23. }
  24. }

日志互相覆盖的问题

使用锁的方式,虽然能解决问题,但性能有问题

  1. public class Logger {
  2. private FileWriter writer;
  3. public Logger() {
  4. File file = new File("/User/cxw/log.txt");
  5. writer = new FileWriter(file,true);//
  6. }
  7. public void log(String message) {
  8. synchronized(Logger.class) {
  9. writer.write(message);
  10. }
  11. }
  12. }
  1. public class Logger {
  2. private FileWriter writer;
  3. private static final Logger instance = new Logger();
  4. private Logger() {
  5. File file = new File("/User/cxw/log.txt");
  6. writer = new FileWriter(file,true);//
  7. }
  8. public static Logger getInstance() {
  9. return instance;
  10. }
  11. }

二、表示全局唯一类

  1. import java.util.concurrent.atomic.AtomicLong;
  2. public class IdGenerator {
  3. private AtomicLong id = new AtomicLong(1);
  4. private static final IdGenerator instance = new IdGenerator();
  5. private IdGenerator() {}
  6. public static IdGenerator getInstance() {
  7. return instance;
  8. }
  9. public long getId() {
  10. return id.incrementAndGet();
  11. }
  12. }
  13. // IdGenerator使用举例
  14. long id = IdGenerator.getInstance().getId();

单例存在哪些问题?

单例存在的问题

  1. 对OOP特性的支持不友好
  2. 单例会隐藏类之间的依赖关系
  3. 对代码的扩展性不友好
  4. 单例对代码的可测试性不友好
  5. 单例不支持有参数的构造函数

有何替代解决方案


如何设计实现一个集群环境下的分布式单例模式?

  1. 如何理解单例模式中的唯一性?
  2. 如何实现线程唯一的单例?
  3. 如何实现集群环境下的单例?
  4. 如何实现一个多例模式?