设计模式(单例和简单代理)
一、单例(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();
这段代码其实是分为三步执行:
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 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();
}
}
运行结果
代理面谈...
代理签合同...
代理订票...
真实角色(周杰伦)唱歌...
代理收钱...