欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

常用设计模式

时间:2023-06-20
一、单例模式 1、类图  2、时序图 3、实现代码:

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)会倾向于使用静态内部类,
//    如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。




}

  4、测试类:

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 singletonHashMap = new 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("【不存在重复的单例对象】");
        }
    }
}

二、代理模式 1、类图 2、时序图  3、代码实现

//抽象主题
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("访问真实主题之后的后续处理。");
    }
}

4、测试类


public class ProxyPattern {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。