Exija um:
Certifique-se de que haja apenas uma instância da classe em uma JVM (compartilhamento multi-thread)
solução
1) Certifique-se de que o mundo externo da classe não deve construir diretamente o objeto
2) Colocar a instância da classe no pool? (Pool de inteiros, pool de strings, ...)
Plano de pouso:
Solução 1: criar objetos de instância quando a classe é carregada, cenários de aplicação: objetos pequenos (menor uso de memória)
---> Estilo chinês Hungry (thread safety, alta eficiência de chamada, mas sem atraso de carregamento):
class Singleton01{
//private byte[]array=new byte[1024*1024];
//1.构建方法私有化
private Singleton01() {}
//2.类的内部构建对象
private static Singleton01 instance=new Singleton01();
//3.对外提供对象访问
public static Singleton01 getInstance() {
return instance;
}
public static void show() {}
public void display() {}
}
Pensando: Se o método show for chamado muitas vezes, e o método display for chamado no final ou raramente, isso causará a criação do objeto Singleton01 no início. Se o objeto for grande, isso causará um desperdício de recursos.
Solução 2: Quando e quando criar uma instância da classe , cenário de aplicação: objetos grandes (ocupam mais memória), raramente usados;
---> Estilo lento (thread safety, baixa eficiência de chamada, mas pode ser carregado com atraso):
class Singleton02{
//private byte[]array=new byte[1024*1024];
//1.构建方法私有化
private Singleton02() {}
//2.类的内部构建对象
private static Singleton02 instance;
//3.对外提供对象访问(会有阻塞)
public synchronized static Singleton02 getInstance() {
if(instance==null) {
System.out.println("create()");
instance=new Singleton02();
}
return instance;
}
public static void show() {}
public void display() {}
}
Pensando: No caso de multithreading, bloqueios são necessários. Se o código a seguir não estiver bloqueado, o objeto será construído duas vezes. Multithreading com bloqueios bloqueará
static void doMethod02() {
Thread t1=new Thread() {
@Override
public void run() {
Singleton02.getInstance();
Singleton02.getInstance();
}
};
Thread t2=new Thread() {
@Override
public void run() {
Singleton02.getInstance();
Singleton02.getInstance();
}
};
t1.start();
t2.start();
}
public static void main(String[] args) {
doMethod02();
}
Resultado de saída:
create()
create()
Opção 3: Reduzir o bloqueio com base na Opção 2, a verificação dupla reduz muito o bloqueio
---> Double CheckLock realiza um único caso: DCL é o mecanismo de julgamento de bloqueio duplo ( devido ao modelo JVM subjacente, podem ocorrer problemas ocasionais, portanto, não é recomendado ):
O papel da palavra-chave volatile?
1) Garanta a visibilidade das variáveis entre vários threads (um thread modifica o valor desta variável e os outros são imediatamente visíveis)
-------- Artigo de referência: https://blog.csdn.net/qianzhitu/article/details/103052040
2) A reordenação de instruções é proibida (a JVM otimizou a execução de instruções)
class Singleton03{//应用场景:大对象(占用内存比较多),稀少用
//private byte[]array=new byte[1024*1024];
//1.构建方法私有化
private Singleton03() {}
//2.类的内部构建对象
private static volatile Singleton03 instance;
//3.对外提供对象访问(会有阻塞)
//3.1多个线程并发访问此方法是否会有线程不会被阻塞?(有的)
//3.2为什么synchronized内部还要有一次判断?(确保对象只创建1次)
public static Singleton03 getInstance() {
if(instance==null) {
synchronized(Singleton03.class) {
System.out.println("synchronized");
if(instance==null) {
//System.out.println("create()");
instance=new Singleton03();
//对象创建过程(开辟内存,初始化属性,调用构造方法,为instance赋值)
}
}
}
return instance;
}
}
Solução 4: implemente o carregamento lento de objetos com base em classes internas. Com base na solução 3, continue a reduzir o congestionamento enquanto otimiza o uso de recursos. Cenários de aplicativos: objetos grandes (carregamento lento), usados com frequência
---> Modo de implementação de classe interna estática (segurança de thread, alta eficiência de chamada, carregamento de atraso)
Nota: Apenas variáveis estáticas podem ser escritas em classes internas estáticas, e variáveis estáticas não podem ser escritas em classes internas não estáticas (a sintaxe é assim)
class Singleton04{
//private byte[] array=new byte[1024*1024];
private Singleton04() {}
//Singleton04加载时不会加载Inner类
private static class Inner{
private static final Singleton04 instance=new Singleton04();
}
//可以频繁访问(没有阻塞)
public static Singleton04 getInstance() {
//基于内部类实现对象的延迟加载
return Inner.instance;
}
//public static void show() {}
//public void display() {}
}
Pensando: quando o método show é chamado, ele não fará com que a variável do array seja inicializada, nem carregue a classe Inner; quando a instância é obtida chamando o método getInstance, chamar Inner.instance fará com que a classe interna estática Inner seja carregada, inicialize a instância e execute new Singleton04 (), faz com que o array seja inicializado e realiza o carregamento lento de objetos grandes.
Esquema 5: Com base na enumeração; cenário de aplicação: pequenos objetos, usados com frequência
---> Classe de enumeração (segurança de thread, alta eficiência de chamada, não pode ser carregado com atraso, pode impedir naturalmente chamadas de reflexão e desserialização)
enum Singleton05{//Singleton05.class
INSTANCE;//此对象可以延迟加载吗?不可以
//private byte[] array=new byte[1024*1024];
public static void show() {}
}
transferir:
static void doMethod05() {
Singleton05.INSTANCE.show();
}
Pensando: O objeto é construído quando a classe de objeto de enumeração é carregada e o carregamento lento não pode ser feito. Se for uma grande matriz de objetos, ocupará recursos quando a classe for carregada, o que não é adequado; portanto, só é adequado para objetos pequenos e cenários usados com frequência;
Como escolher:
- Objetos singleton ocupam menos recursos, não precisam atrasar o carregamento, a enumeração é melhor do que fome
-Os objetos singleton ocupam muitos recursos e precisam ser carregados com um atraso. As classes internas estáticas são melhores do que as preguiçosas
Exigir dois:
Certifique-se de que haja apenas uma instância da classe em um encadeamento (singleton encadeado)
Afirmação:
1) Este tipo de instância tem apenas uma cópia por thread (um singleton dentro do thread)
2) Este objeto não pode ser construído fora
Destino da solução: baseado na implementação ThreadLocal
package com.java.design;
class Looper{//迭代器(任意的一个对象)
private Looper() {
System.out.println("Looper()");
}
private static ThreadLocal<Looper> td=new ThreadLocal<>() ;
public static Looper getLooper() {
//1.从当前线程获取looper对象
Looper looper=td.get();//key是谁?
//2.当前线程没有则创建looper并绑定到当前线程
if(looper==null) {
looper=new Looper();
td.set(looper);//key是谁?ThreadLocal
}
//3.返回looper实例
return looper;
}
}
public class TestSingleton02 {
public static void main(String[] args) {
for(int i=0;i<3;i++) {
new Thread() {
@Override
public void run() {
Looper.getLooper();
Looper.getLooper();
Looper.getLooper();
}
}.start();
}
}
}
Resultado de saída:
Looper()
Looper()
Looper()