English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Análisis detallado del patrón de diseño Singleton en programación Android

Este artículo describe un ejemplo de patrón de diseño Singleton en programación Android. Compartimos esto con todos para su referencia, como se detalla a continuación:

Uno, introducción

El patrón Singleton es uno de los patrones de aplicación más amplios, y también puede ser el único patrón de diseño que muchos ingenieros principiantes utilizan. Al aplicar este patrón, la clase del objeto Singleton debe garantizar que solo exista una instancia. Muchas veces, todo el sistema solo necesita tener un objeto global, lo que nos ayuda a coordinar el comportamiento del sistema en su conjunto.

Dos, definición

Se asegura de que una certaine clase tenga solo una instancia y se instancie por sí misma proporcionando esta instancia a todo el sistema.

Tres, escena de uso

Escena en la que se asegura de que un certaine clase tenga solo un objeto, evitando así la creación de múltiples objetos que consuman muchos recursos, o que solo debe haber un certaine tipo de objeto. Por ejemplo, si crear un objeto requiere muchos recursos, como acceder a recursos de E/S y bases de datos, en este caso, se debe considerar el uso del patrón Singleton.

Cuatro, métodos de implementación

1、模式饿汉

Ejemplo de código:

/**
 * 模式饿汉
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance(){
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

ventajas:Carga diferida (carga solo cuando se necesita)

desventajas:Inseguro en hilos, es fácil que se des sincronicen en entornos de múltiples hilos, como operaciones de escritura y lectura frecuentes en objetos de base de datos.

2、模式懒汉

Ejemplo de código:

/**
 * 模式懒汉
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static synchronized Singleton getInstance(){
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

En comparación con el patrón de hambre, se agrega la palabra clave synchronized al método getInstance(), lo que significa que getInstance es un método sincronizado, lo que asegura la unicidad del objeto singleton en el caso de múltiples hilos. Sin embargo, al reflexionar sobre esto, es posible que encuentres un problema, incluso si instance ya ha sido inicializado (instance se inicializará por primera vez en la llamada), cada vez que se llama al método getInstance se realiza una sincronización, lo que consume recursos innecesarios, que es el problema más grande del patrón de perezoso.}

ventajas:Resuelve el problema de inestabilidad en el hilo.

desventajas:Es necesario realizar la instanciación a tiempo durante la carga por primera vez, la respuesta es ligeramente lenta, y el problema más grave es que cada llamada a getInstance realiza una sincronización, lo que causó un costo de sincronización innecesario.

Suplemento:En el código fuente de Android, métodos de singleton utilizados incluyen InputMethodManager, AccessibilityManager, etc., que utilizan este patrón de singleton

3、Double Check Lock (DCL) comprobación doble

Ejemplo de código:

/**
 * Patrón de singleton con comprobación双重检查锁定(DCL)
 */
public class Singleton {
  private static Singleton instance;
  private Singleton(){}
  public static Singleton getInstance(){
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

El punto fuerte de este programa está naturalmente en el método getInstance, donde se puede ver que getInstance realiza dos juicios de nulo sobre instance: el primer nivel de juicio es principalmente para evitar la sincronización innecesaria, y el segundo nivel de juicio es para crear la instancia en caso de null.

Supongamos que el hilo A ejecuta la instrucción instance = new Singleton(), aquí parece ser una sola instrucción, pero en realidad no es una operación atómica; esta instrucción finalmente será compilada en múltiples instrucciones de ensamblaje, que básicamente hace3Una cosa es

(1( ) Asigna memoria a una instancia de Singleton;

(2()) Llama al constructor Singleton(), inicializa los campos miembros;

(3()) apunta al espacio de memoria asignado al objeto instance (en este momento, instance ya no es null).

Sin embargo, debido a que el compilador de Java permite que el procesador ejecute de manera desordenada, así como a JDK1.5en la regulación de la secuencia de escritura de Cache, registro a la memoria principal en el modelo de memoria de Java (JMM, por sus siglas en inglés) mencionada anteriormente, la secuencia de las segundas y terceras frases no puede garantizarse. Es decir, la secuencia de ejecución puede ser1-2-3también puede ser1-3-2Si es el caso, y además antes de que3Finalizado,}}2antes de que se ejecute, se cambie a la línea B, en este momento, instance ya se ha ejecutado el tercer punto en la línea A, instance ya no es nulo, por lo que la línea B toma directamente instance, y cuando se utiliza, se producirá un error, este es el problema de fallo de DCL, y este error difícil de rastrear y reproducir puede estar oculto por mucho tiempo.

en JDK1.5Después de eso, la empresa SUN ha prestado atención a este problema, ajustado el JVM, especificado la palabra clave volatile, por lo que si el JDK es1.5o versiones posteriores, simplemente cambiando la definición de instance a private volatile static Singleton instance puede garantizar que instance se lea siempre desde la memoria principal, y se puede completar el patrón de singleton utilizando la escritura de DCL. Por supuesto, el uso de volatile puede afectar ligeramente el rendimiento, pero considerando la corrección del programa, es merecedor de sacrificar este rendimiento.

ventajas:La utilización de recursos es alta, el objeto singleton solo se instanciará cuando se ejecute getInstance por primera vez, lo que es eficiente. En situaciones con baja cantidad de concurrencia y baja seguridad, el patrón de singleton puede funcionar perfectamente.

desventajas:La reacción es ligeramente lenta al cargar por primera vez, y también puede fallar ocasionalmente debido a las razones del modelo de memoria de Java. También tiene ciertos defectos en entornos de alta concurrencia, aunque la probabilidad es muy baja.

Suplemento:En el proyecto de código abierto de imágenes para Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)se utiliza este método.
El patrón DCL es la forma de implementación de singleton más utilizada, que puede instanciar el objeto singleton solo cuando es necesario y garantizar la unicidad del objeto singleton en la mayoría de los escenarios, a menos que tu código sea bastante complejo en términos de ejecución en paralelo o inferior a JDK6usar, de lo contrario, este método generalmente puede satisfacer las necesidades.

4、Patrón de singleton de clase interna estática

DCL, aunque en cierta medida resuelve problemas como el consumo de recursos, la sincronización innecesaria y la seguridad en la ejecución en paralelo, aún así, en ciertas situaciones, puede fallar. Este problema se conoce como el fallo de doble comprobación de bloqueo (DCL), se mencionó este problema en el último capítulo del libro 'Prácticas de Programación Concurrency en Java', y se señaló que esta 'optimización' es fea y no se recomienda su uso. En su lugar, se sugiere reemplazar con el siguiente código:

Ejemplo de código:

/**
 * Patrón de singleton de clase interna estática
 */
public class Singleton {
  private Singleton(){}
  public static Singleton getInstance(){
    return SingletonHolder.instance;
  }
  /**
   * Clase interna estática
   * Carga diferida, reducción de consumo de memoria
   */
  private static class SingletonHolder{}}
    private static final Singleton instance = new Singleton();
  }
}

Cuando se carga por primera vez la clase Singleton, no se inicializa instance, solo cuando se llama por primera vez al método getInstance de Singleton se inicializa instance. Por lo tanto, la primera llamada al método getInstance causará que el motor de virtualización cargue la clase SingletonHolder, este método no solo asegura la seguridad en términos de hilos, sino que también garantiza la unicidad del objeto singleton, al mismo tiempo retrasa la instanciación del singleton, por lo que es la forma de implementación del patrón singleton recomendada.

ventajas:carga diferida, segura en términos de hilos (el carga de clase en java es mutua), y también reduce el consumo de memoria

5、singleton enum

Se han explicado algunas formas de implementación del patrón singleton, pero estas formas de implementación no son tan complicadas ni presentan problemas en ciertas situaciones.

Ejemplo de código:

/**
 * patrón singleton enum
 */
public enum Singleton {
  /**
   * 1.desde Java1.5comienza a soportar;
   * 2.ofrece de manera gratuita el mecanismo de serialización;
   * 3.absolutamente evita la instanciación múltiple, incluso frente a ataques de serialización o reflexión complejos;
   */
  instance;
  private String others;
  Singleton() {
  }
  public String getOthers() {
    return others;
  }
  public void setOthers(String others) {
    this.others = others;
  }
}

La simplicidad de la escritura es la mayor ventaja del singleton enum, ya que el enum en Java es idéntico a una clase común, y puede tener campos y métodos propios. Lo más importante es que la creación de instancias de enum por defecto es segura en términos de hilos, y siempre es un singleton en cualquier situación.

¿Por qué se dice esto? En varios casos de implementación del patrón singleton, se crea una nueva instancia en una situación específica, es decir, durante la deserialización.

Al serializar, se puede escribir una instancia de objeto singleton en el disco, luego leerla de vuelta, y así obtener una instancia de manera efectiva. Incluso si el constructor es privado, durante la deserialización se puede crear una nueva instancia de la clase mediante métodos especiales, lo que es equivalente a llamar al constructor de la clase. La operación de deserialización proporciona un hook especial, una método privado e instanciado en la clase readResolve(), que permite a los desarrolladores controlar la deserialización del objeto. Por ejemplo, si se desea evitar que el objeto singleton se regenere durante la deserialización, debe agregarse el siguiente método:

private Object readResolve() throws ObjectStreamException {}}
  return instance;
}

es decir, regresar el objeto instance en el método readResolve, en lugar de generar un nuevo objeto por defecto. Para los enumerados, no existe este problema, porque incluso si se deserializa no se genera una nueva instancia.

ventajas: ofrece gratuitamente un mecanismo de serialización, que previene absolutamente la instanciación multiple, incluso frente a ataques de serialización o reflexión complejos.

desventajas: desde Java1.5se comenzó a soportar.

Se ha hablado principalmente del patrón Singleton5métodos de creación, los usuarios pueden usarlos según sus ventajas y desventajas en proyectos personales.

Los lectores interesados en más contenido relacionado con Android pueden consultar las secciones especiales de este sitio: 'Tutorial de inicio y avanzado de desarrollo de Android', 'Consejos de depuración y resumen de métodos de solución de problemas comunes de Android', 'Resumen de uso de componentes básicos de Android', 'Resumen de técnicas de vista View de Android', 'Resumen de técnicas de diseño de layout de Android' y 'Resumen de uso de controles de Android'.

Espero que lo descrito en este artículo pueda ayudar a todos a diseñar programas Android.

Declaración: el contenido de este artículo se ha obtenido de la red, es propiedad del autor original, el contenido ha sido contribuido y subido por usuarios de Internet, este sitio no posee los derechos de propiedad, no ha sido editado por humanos y no asume ninguna responsabilidad legal. Si encuentra contenido sospechoso de copyright, le invitamos a enviar un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @) para denunciar, y proporcione evidencia relevante. Una vez verificada, este sitio eliminará inmediatamente el contenido sospechoso de infracción.

Te gustará