package com.example.pattern.Singleton;
public class Singleton {
private static Singleton instance;
private Singleton (){}
// 0、原始写法
// 这段代码简单明了,而且使用了懒加载模式,
//但是却存在致命的问题。当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。
//也就是说在多线程下不能正常工作。
public static Singleton getInstance() {
// if 判断与 new 对象 不是原子操作 导致线程安全
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 1、懒汉式,线程安全
// 最简单的方法是将整个 getInstance() 方法设为同步(synchronized)
// 虽然做到了线程安全,并且解决了多实例的问题,但是它并不高效。
//因为在任何时候只能有一个线程调用 getInstance() 方法。
public static synchronized Singleton getInstanceForThreadSafe() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 2、双重检验锁(double checked locking) DCL
//是一种使用同步块加锁的方法。程序员称其为双重检查锁,因为会有两次检查 instance == null,
//一次是在同步块外,一次是在同步块内。为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if,
private volatile static Singleton instanceDCL; //声明成 volatile
public static Singleton getSingleton() {
if (instanceDCL == null) { // 多个线程执行到此处 //Single Checked
synchronized (Singleton.class) {
if (instanceDCL == null) { //Double Checked
// 这里也有问题:主要在于instance = new Singleton()这句,
// 这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。
// 1、给 instance 分配内存
// 2、调用 Singleton 的构造函数来初始化成员变量
// 3、将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
// 但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,
// 最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,
// 这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。
// 只需要将 instance 变量声明成 volatile 就可以了。
instanceDCL = new Singleton();
}
}
}
return instanceDCL ;
}
// 3、饿汉式 static final field
// 单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。
//类加载时就初始化
// 单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。
// 饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,
// 在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。
private static final Singleton instanceStatic = new Singleton();
public static Singleton getInstanceStatic(){
return instanceStatic;
}
// 4、静态内部类 static nested class
// 使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,
// 除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstanceClass() {
return SingletonHolder.INSTANCE;
}
// 5、枚举 Enum
// 一般来说,单例模式有五种写法:懒汉、饿汉、双重检验锁、静态内部类、枚举。
// 一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类,
// 如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。
}
package com.example.pattern.Singleton;
import org.junit.Test;
import java.util.HashMap;
import java.util.HashSet;
public class SingletonTest {
final static Integer ThreadCount = 10;
HashMap
@Test
public void test(){
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1);
System.out.println(singleton2);
System.out.println(singleton1==singleton2);
}
// 0、原始写法 存在线程安全
@Test
public void testTheadSafe() throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton);
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getInstance();
System.out.println(singleton);
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
// 1、懒汉式,线程安全解决
@Test
public void testTheadSafeResolve() throws InterruptedException {
for (int i=0; i< ThreadCount; i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getInstanceForThreadSafe();
System.out.println(singleton);
singletonHashMap.put(Thread.currentThread().getName(),singleton);
}
},"ThreadId="+Thread.currentThread().getName());
thread.start();
thread.join();
}
HashSet hashSet = new HashSet(singletonHashMap.values());
if (hashSet.size() != singletonHashMap.values().size() ){
System.err.println("存在重复的单例对象");
}else {
System.out.println("不存在重复的单例对象");
}
}
// 2、双重检验锁(double checked locking) DCL
@Test
public void testDCL() throws InterruptedException {
for (int i=0; i< ThreadCount; i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
singletonHashMap.put(Thread.currentThread().getName(),singleton);
}
},"ThreadId="+Thread.currentThread().getName());
thread.start();
thread.join();
}
HashSet hashSet = new HashSet(singletonHashMap.values());
if (hashSet.size() != singletonHashMap.values().size() ){
System.err.println("存在重复的单例对象");
}else {
System.out.println("不存在重复的单例对象");
}
}
// 3、饿汉式 static final field
@Test
public void testStatic() throws InterruptedException {
for (int i=0; i< ThreadCount; i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getInstanceStatic();
System.out.println(singleton);
singletonHashMap.put(Thread.currentThread().getName(),singleton);
}
},"ThreadId="+Thread.currentThread().getName());
thread.start();
thread.join();
}
HashSet hashSet = new HashSet(singletonHashMap.values());
if (hashSet.size() != singletonHashMap.values().size() ){
System.err.println("存在重复的单例对象");
}else {
System.out.println("不存在重复的单例对象");
}
}
// 4、静态内部类 static nested class
@Test
public void testStaticClass() throws InterruptedException {
for (int i=0; i< ThreadCount; i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Singleton singleton = Singleton.getInstanceClass();
System.out.println(singleton);
singletonHashMap.put(Thread.currentThread().getName(),singleton);
}
},"ThreadId="+Thread.currentThread().getName());
thread.start();
thread.join();
}
HashSet hashSet = new HashSet(singletonHashMap.values());
if (hashSet.size() != singletonHashMap.values().size() ){
System.err.println("存在重复的单例对象");
}else {
System.out.println("【不存在重复的单例对象】");
}
}
}
//抽象主题
public interface Subject {
void request();
}
//真实主题
public class RealSubject implements Subject {
public void request() {
System.out.println("访问真实主题方法...");
}
}
//代理对象
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
public void request() {
preRequest();
realSubject.request();
afterRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void afterRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
public class ProxyPattern {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.request();
}
}