1. Descrição
1. Conceitos básicos de singleton
Haverá apenas um objeto de instância no Jvm atual
2. Cenários de aplicação singleton
1. O arquivo de configuração definido no projeto 2.
Objeto servlet é singleton por padrão
3. Pool de threads, pool de conexão de banco de dados
4. O objeto Bean no Spring é singleton por padrão
5. Implementar contador de site
6. Estrutura de cache integrada Jvm (definir singleton HashMap)
7. Definir informações constantes enum
3 / Vantagens e desvantagens do Singleton
Vantagens: Pode salvar a memória heap atual, não precisa de novos objetos com frequência e pode ser acessado rapidamente.
Desvantagens: Quando vários threads acessam o mesmo objeto singleton, pode haver problemas de segurança de thread.
Duas, 7 maneiras de criar o modo de coluna única
- Se você adicionar reflexão para criar + serialização criar , para os nove tipos de criar uma maneira (para quebrar uma única coluna recriada)
- Se você adicionar o construtor para criar , para os 10 tipos de maneira de criar
- O objetivo principal do construtor usando modificação privada é evitar o uso direto externo do novo objeto para inicializar e alterar a classe, tornando-se múltiplas colunas
1. Homem preguiçoso (quando simultaneamente, há falhas de segurança)
package com.xijia;
import java.io.Serializable;
/**
* 单例模式(懒汉式)
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 9:49
* @version 1.0.0
*/
public class Singleton01 implements Serializable {
private static Singleton01 singleton01 = null;
/**
* 私有化,禁止new 改对象
*/
private Singleton01() {
}
/**
* 此创建方法 缺陷: 如果两个线程同时进入该 getInstance()方法,会破坏数据初始化的数据
* 如果: singleton01为计数器或火车票
* ----a执行 初始化 singleton01 = 1
* ----b执行 初始化 singleton01 = 1
* 实际结果应该为2,但实际结果为1 ,有兴趣可自行模拟
* @return
*/
public static Singleton01 getInstance() {
if (singleton01 == null) {
singleton01 = new Singleton01();
}
return singleton01;
}
public static void main(String[] args) {
// 初始化
Singleton01 instance1 = Singleton01.getInstance();
// 直接获取,不初始化, 但是遇到instance1和instance2 的调用同时进入if 条件,部分业务数据将不正确(如计数器,火车票等)
Singleton01 instance2 = Singleton01.getInstance();
System.out.println(instance1 == instance2);
}
}
2. Mecanismo de bloqueio lento + sincronizado (não há falha de segurança, mas o desempenho é baixo)
package com.xijia;
/**
* 单列模式(懒汉式 + synchronized 锁机制)
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 9:58
* @version 1.0.0
*/
public class Singleton02 {
private static Singleton02 singleton02;
private Singleton02() {
}
/**
* 初始化 singleton02, 改方式避免了案例一 计数器重复初始化导致结果错误的问题,但存在一定的缺陷
* 缺陷:
* 初始化singleton02 无缺陷
* 但 获取singleton02 数据时出现缺陷, 当我们获取singleton02时,两个线程同时获取singleton02时,必须排队获取singleton02,这显然是会严重影响性能的
* @return
*/
public static synchronized Singleton02 getInstance() {
if (singleton02 == null) {
singleton02 = new Singleton02();
}
return singleton02;
}
public static void main(String[] args) {
// 初始化singleton02
Singleton02 instance1 = Singleton02.getInstance();
// 直接获取,不初始化
Singleton02 instance2 = Singleton02.getInstance();
System.out.println(instance1 == instance2);
}
}
3. Homem preguiçoso + trava de inspeção dupla (sem defeito + melhor desempenho)
package com.xijia;
/**
* 单列模式(懒汉式 + 双重检验锁)
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 10:06
* @version 1.0.0
*/
public class Singleton03 {
private static Singleton03 singleton03;
private Singleton03() {
}
/**
* 双重检验锁初始化 singleton03
* 无缺陷
* ---- 修复案例2的 获取数据需要排队的问题,获取数据直接获取,
* ----- 如果遇到多个线程同时进入 if ,加锁排队,第一个进入的线程初始化了对象,下一个排队的人进入后判断发现对象已经初始化了,就不重新初始化了
* @return
*/`在这里插入代码片`
public static Singleton03 getInstance() {
// 上锁(创建该对象) 第一次判断
if (singleton03 == null) {
synchronized (Singleton03.class) {
//第二次判断
if (singleton03 == null) {
singleton03 = new Singleton03();
}
}
}
return singleton03;
}
public static void main(String[] args) {
Singleton03 instance1= Singleton03.getInstance();
Singleton03 instance2= Singleton03.getInstance();
System.out.println(instance1==instance2);
}
}
4. Estilo chinês com fome (variáveis estáticas e comumente usadas, as deficiências não são usadas e ocupam recursos de memória)
package com.xijia;
/**
* 饿汉式
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 10:12
* @version 1.0.0
*/
public class Singleton04 {
/**
* 当class文件被加载的时候就创建该对象,常用于创建常量
*/
public static final Singleton04 singleton04 = new Singleton04();
private Singleton04() {
}
public static void main(String[] args) {
System.out.println(Singleton04.singleton04 == Singleton04.singleton04);
}
}
5. Criação de bloco de método estático (não comumente usado)
package com.xijia;
/**
* 静态方法区
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 10:14
* @version 1.0.0
*/
public class Singleton05 {
private static Singleton05 singleton05 = null;
private Singleton05() {
}
/**
* 只会初始化一次(或常用的 init方法)
*/
static {
System.out.println("当前class被加载");
singleton05 = new Singleton05();
}
public static synchronized Singleton05 getInstance() {
return Singleton05.singleton05;
}
public static void main(String[] args) {
System.out.println(Singleton05.getInstance() == Singleton05.getInstance());
}
}
6. Criação de classe interna estática (comumente usada no Android)
package com.xijia;
/**
* 静态内部类
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 10:16
* @version 1.0.0
*/
public class Singleton06 {
public Singleton06() {
}
/**
* 静态内部类 (安卓常用)
*/
private static class SingletonHolder {
private static final Singleton06 singleton06 = new Singleton06();
}
/**
* 获取静态内部类的 singleton06
* @return
*/
public static Singleton06 getInstance() {
return SingletonHolder.singleton06;
}
public static void main(String[] args) {
Singleton06 instance1 = Singleton06.getInstance();
Singleton06 instance2 = Singleton06.getInstance();
System.out.println(instance1 == instance2);
}
}
7. Criação de enumeração (segurança absoluta de thread)
package com.xijia;
/**
* 枚举方式创建
* @author wangsong
* @date 2020/9/5 0005 10:21
* @return
* @version 1.0.0
*/
public enum Singleton07 {
INSTANCE;
public void addUser() {
System.out.println("我是枚举,我先天性安全");
}
Singleton07() {
System.out.println(">>>Singleton07无参构造函数执行<<");
}
public static void main(String[] args) {
// 只会执行一次构造函数
System.out.println(Singleton07.INSTANCE == Singleton07.INSTANCE);
}
}
Três, quebre a única coluna
1. Rachadura de reflexão
public class Test001 {
public static void main(String[] args) throws Exception {
// 使用反射机制创建我们的对象
Class<?> aClass = Class.forName("com.xijia.Singleton01");
// getDeclaredConstructor();获取当前类(不包含父类),getConstructor 所有的 包含父类构造函数
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
// 走无参构造函数 反射创建对象成功
Singleton01 instance1 = (Singleton01) constructor.newInstance();
Singleton01 instance2 = Singleton01.getInstance();
/**
* 将输出false, 单列对象 Singleton01 被重复创建, singleton01 在静态区的值被重新初始化,原数据将被破坏
*/
System.out.println(instance1 == instance2);
}
}
2. Fissuração de serialização
/**
* 序列化破解单列 (当类添加了 implements Serializable 的,都将可以破解单列)
* @author wangsong
* @date 2020/9/5 0005 10:26
* @return
* @version 1.0.0
*/
public class Test002 {
public static void main(String[] args) throws Exception {
// 1.需要将该对象序列化到本地存放
FileOutputStream fos = new FileOutputStream("d:/code/user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Singleton01 instance1 = Singleton01.getInstance();
oos.writeObject(instance1);
oos.close();
fos.close();
//2.从硬盘中反序列化对象到内存中
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/user.txt"));
Singleton01 instance2 = (Singleton01) ois.readObject();
/**
* 将输出false, 单列对象 Singleton01 被重复创建, singleton01 在静态区的值被重新初始化,原数据将被破坏
*/
System.out.println(instance1==instance2);
}
}
3. Tente quebrar a enumeração por reflexão (não consigo quebrar)
/**
* 方式破解单列(无法破解,直接抛出异常)
* @author wangsong
* @mail [email protected]
* @date 2020/9/5 0005 10:33
* @version 1.0.0
*/
public class Test004 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> aClass = Class.forName("com.xijia.Singleton07");
/**
* 直接抛出,Exception in thread "main" java.lang.InstantiationException: com.xijia.Singleton07 异常
*/
Object o = aClass.newInstance();
}
}
4. Tente serializar e quebrar a enumeração (incapaz de quebrar)
/**
* 序列化破解枚举(先天性安全,无法破解)
* @author wangsong
* @date 2020/9/5 0005 10:30
* @return
* @version 1.0.0
*/
public class Test003 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1.将对象序列化存入到本地文件中
FileOutputStream fos = new FileOutputStream("d:/code/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Singleton07 instance1 = Singleton07.INSTANCE;
oos.writeObject(Singleton07.INSTANCE);
oos.close();
fos.close();
//2.从硬盘中反序列化对象到内存中
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));
Singleton07 instance2 = (Singleton07) ois.readObject();
/**
* 输出true, 枚举类没有被重新创建,原数据绝对安全
*/
System.out.println(instance1 == instance2);
}
}
-
Parte do conteúdo acima vem da Ant Classroom http://www.mayikt.com/
-
Projeto pessoal de código aberto (sistema de gerenciamento universal de fundo) -> https://gitee.com/wslxm/spring-boot-plus2 , você pode conferir se quiser
-
Este é o fim deste artigo. Se você achar útil, dê um like ou preste atenção nele. Continuaremos a atualizar mais conteúdo de vez em quando ... Obrigado por assistir!