设计模式(单例和简单代理)

不羁岁月 提交于 2020-01-22 09:47:14

设计模式(单例和简单代理)

一、单例(Singleton)

确保一个类只有一个实例,并提供该实例的全局访问点。

Ⅰ 饿汉式

顾名思义,饿汉初始化类时,直接静态加载SingletonDemo01类的实例instance不管用到没用到这个实例,都创建出来了,所以没有延时加载的优势。

***评价:**线程不安全的问题主要由于实例化多次才会造成,采取直接实例化不会产生线程不安全问题,但也丢失了延迟实例化带来的节约资源你的好处

/**
 * 测试饿汉式单例模式
 * @author wangqun
 *
 */
public class SingletonDemo01 {
	//类初始化时,立即加载这个对象(没有延时加载的优势!)  由于加载类时天然线程安全!
	private static SingletonDemo01 instance = new SingletonDemo01();
	
	private SingletonDemo01() {
		
	}
	//方法不用加synchronized 效率高!
	public static SingletonDemo01 getInstance() {
		return instance;
	}

}

Ⅱ 懒汉式

类初始化时,没有创建对象,真正使用的时候才创建!

评价: 这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (instance == null) ,并且此时 instance 为 null,那么会有多个线程执行 instance = new SingletonDemo02(); 语句,这将导致实例化多次 instance。

​ 因此本例加了synchronized同步块,多个线程调用会有等待 效率不如饿汉式

/**
 * 测试懒汉式单例模式
 * @author wangqun
 *
 */
public class SingletonDemo02 {
	//类初始化时,未加载这个对象  真正用的时候再创建
	private static SingletonDemo02 instance;
	
	private SingletonDemo02() {
		
	}
	//由于需要加synchronized同步块  多个线程调用会有等待  效率不如饿汉式
	public static synchronized SingletonDemo02 getInstance() {
		if(instance == null) {
			instance = new SingletonDemo02();
		}
		return instance;
	}

}

Ⅲ 双重锁检测

uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。

双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。

if (uniqueInstance == null) {
    synchronized (Singleton.class) {
        uniqueInstance = new Singleton();
    }
}

**评价:**uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

*Ⅳ 静态内部类实现

当SingletonDemo03类被加载时,静态内部类SingletonClassinstance没有被加载,只有调用 getInstance() 时,方法触发SingletonClassinstance.instance;时SingletonClassinstance才被加载,此时初始化实例,并且JVM保证instance被初始化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

/**
 * 静态内部类实现单例模式
 * 1、线程天然安全
 * 2、初始化这个类时候  不会初始化静态内部类  不耗资源
 * 3、只有调用 getInstance()时候才会初始化内部类(延时加载)
 * @author wangqun
 *
 */
public class SingletonDemo03 {
	private static class SingletonClassinstance{
		private static final SingletonDemo03 instance = new SingletonDemo03();
	}

	public static synchronized SingletonDemo03 getInstance() {
		return SingletonClassinstance.instance;
	}

	private SingletonDemo03() {

	}
}

Ⅴ 枚举实现

/**
 * 枚举实现单例模式
 * 优点:实现简单
 * 		 枚举本身就是单例   JVM从根本提供保障,避免反射和反序列化的漏洞
 * 缺点:无法延时加载
 * @author wangqun
 *
 */
public enum SingletonDemo04 {
	//定义一个枚举的元素
	INSTANCE;
	public void SingletonOperation() {
		//功能处理
	}
	
}

二、代理模式

静态代理模式:区分代理对象和真实对象工作内容,把真实对象不需要处理的事件丢给代理

给出一个例子:歌唱明星只唱歌,布置会场之类的工作交给代理

明星的抽象类(需要进行的工作)

public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}

明星类

/**
 * 真实角色
 * @author wangqun
 *
 */
public class RealStar implements Star {

	@Override
	public void confer() {
		System.out.println("真实角色面谈...");
		
	}

	@Override
	public void signContract() {
		System.out.println("真实角色签合同...");
		
	}

	@Override
	public void bookTicket() {
		System.out.println("真实角色订票...");
		
	}

	@Override
	public void sing() {
		System.out.println("真实角色(周杰伦)唱歌...");
		
	}

	@Override
	public void collectMoney() {
		System.out.println("真实角色收钱...");
		
	}

}

代理类

public class Proxy implements Star{
	private Star star;
	
	
	public Proxy(Star star) {
		super();
		this.star = star;
	}

	@Override
	public void confer() {
		System.out.println("代理面谈...");
		
	}

	@Override
	public void signContract() {
		System.out.println("代理签合同...");
		
	}

	@Override
	public void bookTicket() {
		System.out.println("代理订票...");
		
	}

	@Override
	public void sing() {
		star.sing();
		
	}

	@Override
	public void collectMoney() {
		System.out.println("代理收钱...");
		
	}

}

测试类

public class Client {
	public static void main(String[] args) {
		Star JayZhou = new RealStar();
		Star proxy = new Proxy(JayZhou);
		proxy.confer();
		proxy.signContract();
		proxy.bookTicket();
		proxy.sing();
		
		proxy.collectMoney();
		
	}
}

运行结果

代理面谈...
代理签合同...
代理订票...
真实角色(周杰伦)唱歌...
代理收钱...

参考文章

CyC2018/CS-Notes.

如有错误,敬请指正!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!