Compartir tecnología

Patrón de diseño singleton

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

El patrón singleton garantiza que una clase tenga solo una instancia, se instancia a sí misma y proporciona esta instancia a todo el sistema.
La función principal del patrón singleton es garantizar que solo exista una instancia de una clase.

escenas a utilizar:
● Como generador de números de serie
● Contadores de páginas web, para evitar agregar tecnología a la base de datos cada vez que se actualiza, primero almacene en caché
● Crear objetos que consuman demasiados recursos, como conexiones de E/S y bases de datos, etc.

Hay 7 métodos de implementación del patrón singleton.
Dividido principalmente en estilo Hungry Man y estilo Lazy Man.
Estilo chino hambriento: la carga de clases hará que se cree el objeto de instancia única
Estilo diferido: la carga de clases no hace que se cree el objeto de instancia única, sino que se crea cuando el objeto se usa por primera vez.

Aquí hay una demostración de la mejor manera de implementar el patrón singleton: implemente el patrón singleton en forma de enumeración y úselo (implementando un generador de números de serie)

public enum SequenceGenerator {
    INSTANCE;

    private int sequenceNumber;

    // 构造函数,初始化序列号为0
    private SequenceGenerator() {
        this.sequenceNumber = 0;
    }

    /**
     * 获取下一个序列号。
     * @return 下一个序列号
     */
    public synchronized int getNextSequenceNumber() {
        return sequenceNumber++;
    }
}

// 测试类
public class SequenceGeneratorTest {
    public static void main(String[] args) {
        // 创建一个线程安全的列表来存储生成的序列号
        List<Integer> sequenceNumbers = new CopyOnWriteArrayList<>();

        // 创建多个线程来测试并发生成序列号
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            executorService.submit(() -> {
                int seq = SequenceGenerator.INSTANCE.getNextSequenceNumber();
                sequenceNumbers.add(seq);
            });
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出所有生成的序列号,检查是否有重复
        System.out.println(sequenceNumbers);
        System.out.println("Total generated sequences: " + sequenceNumbers.size());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

La salida resultante es
Insertar descripción de la imagen aquí

En este ejemplo, SequenceGenerator es una clase de enumeración que tiene un campo de número de secuencia privado que se utiliza para almacenar el número de secuencia actual. El método getNextSequenceNumber es seguro para subprocesos porque se declara sincronizado, lo que garantiza que no se produzcan conflictos de números de secuencia en un entorno de subprocesos múltiples.
En la clase SequenceGeneratorTest, usamos un ExecutorService para simular un entorno concurrente y usamos un CopyOnWriteArrayList seguro para subprocesos para recopilar los números de secuencia generados para verificar la exactitud de los números de secuencia.
Tenga en cuenta que, aunque la palabra clave sincronizada garantiza la seguridad de los subprocesos, puede afectar el rendimiento en escenarios de alta concurrencia. Si el generador de números de secuencia necesita funcionar con una concurrencia muy alta, es posible que deba considerar un algoritmo más eficiente, como el uso de variables atómicas (como AtomicInteger) o un esquema de generación de números de secuencia distribuidos.