Creation: Singleton Design Pattern 1

Catalog introduction
  • 01. Introduction to the singleton pattern
  • 02. Singleton pattern definition
  • 03. Singleton usage scenarios
  • 04. Think about a few questions
  • 05. Why use a singleton
  • 06. Handle resource access violations
  • 07. Represents a globally unique class

01. Introduction to the singleton pattern

  • The singleton pattern is the most widely used pattern

    • It is also the first design pattern I know. Before I get to know the singleton pattern in depth, whenever I encounter the code that creates an instance such as getInstance(), I will regard it as an implementation of the singleton pattern.
  • Singleton pattern features

    • The constructor is not open to the outside world, generally private
    • Return a singleton class object via a static method or enumeration
    • Make sure that there is only one object of the singleton class, especially in a multi-threaded environment
    • Make sure that singleton class objects don't reconstruct objects when deserializing

02. Singleton pattern definition

  • Guarantees only one instance of a class and provides a global access point to it

03. Singleton usage scenarios

  • An instance object in an application needs to be accessed frequently.
  • Only one instance exists in the application per launch. Such as account system, database system.

04. Think about a few questions

  • There are many articles on the Internet explaining the singleton pattern, but most of them focus on explaining how to implement a thread-safe singleton. The key point is to clarify the following issues.

    • Why use a singleton?
    • What are the problems with singletons?
    • Difference between singleton and static class?
    • What are the alternative solutions?

05. Why use a singleton

  • The Singleton Design Pattern is very simple to understand.

    • A class is only allowed to create one object (or instance), then this class is a singleton class, this design pattern is called the singleton design pattern, referred to as the singleton pattern.
  • Focus on, why do we need the singleton design pattern? What problems can it solve? Next, I will explain through two practical cases.

    • The first is to handle resource access conflicts;
    • The second is to represent a globally unique class;

06. Handle resource access violations

  • Practical Case 1: Handling Resource Access Conflicts

    • Let's look at the first example. In this example, we custom implemented a Logger class that prints logs to a file. The specific code implementation is as follows:

       public class Logger {
      private FileWriter writer;
      
      public Logger() {
      File file = new File("/Users/wangzheng/log.txt");
      writer = new FileWriter(file, true); //true表示追加写入
      }
      
      public void log(String message) {
      writer.write(mesasge);
      }
      }
      
      // 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());
      }
      }
  • After reading the code, don't worry about my explanation below, you can think about what is wrong with this code.
  • In the above code, we notice that all logs are written to the same file /Users/wangzheng/log.txt. In UserController and OrderController, we create two Logger objects respectively. In the servlet multi-threaded environment of the web container, if two servlet threads execute the login() and create() functions at the same time, and write logs to the log.txt file at the same time, it is possible that the log information overlaps each other. Happening.
  • Why do they overlap each other? We can understand by analogy. In a multi-threaded environment, if two threads add 1 to the same shared variable at the same time, because the shared variable is a competing resource, the final result of the shared variable may not be 2, but only 1. In the same way, the log.txt file here is also a competing resource. Two threads write data to it at the same time, which may overlap each other.
  • So how to solve this problem? The first thing we think of is to add a lock: add a mutex to the log() function (the keyword synchronized in Java can be used), and only one thread is allowed to call and execute the log() function at the same time. The specific code implementation is as follows:

     public class Logger {
      private FileWriter writer;
    
      public Logger() {
        File file = new File("/Users/wangzheng/log.txt");
        writer = new FileWriter(file, true); //true表示追加写入
      }
      
      public void log(String message) {
        synchronized(this) {
          writer.write(mesasge);
        }
      }
    }
  • However, if you think about it, can this really solve the problem of overwriting each other when multiple threads write to the log?

    • the answer is negative. This is because this kind of lock is an object-level lock, and an object calling the log() function at the same time under different threads will be forced to execute sequentially. However, different objects do not share the same lock. Under different threads, the log() function is called and executed through different objects, the lock will not work, and there may still be a problem that the write logs overwrite each other.

07. Represents a globally unique class

  • From a business concept, if some data should only be stored once in the system, it is more suitable to design it as a singleton class.
  • For example, the configuration information class. In the system, we only have one configuration file. After the configuration file is loaded into the memory, it exists in the form of an object, and there should be only one copy.
  • Another example is the unique incremental ID number generator. If there are two objects in the program, there will be situations where duplicate IDs are generated. Therefore, we should design the ID generator class as a singleton.

     import java.util.concurrent.atomic.AtomicLong;
    public class IdGenerator {
      // AtomicLong是一个Java并发库中提供的一个原子变量类型,
      // 它将一些线程不安全需要加锁的复合操作封装为了线程安全的原子操作,
      // 比如下面会用到的incrementAndGet().
      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();
      }
    }
    
    // IdGenerator使用举例
    long id = IdGenerator.getInstance().getId();
  • In fact, the two code examples (Logger, IdGenerator) mentioned today are not elegantly designed, and there are still some problems.

more content


杨充
221 声望42 粉丝

引用和评论

0 条评论