瞎述
作为java工程师,设计模式相当于一个礼仪文化,善于利用礼仪文化的人,无论从哪方面讲都是让人觉得安逸。
--------------------设计模式主要分为:创建型、结构型、行为型-----------
(一)所谓创建型,就包括:
“单例”、“简单工厂”、“工厂方法”、“抽象工厂”、“生成器”、“原型”
(二)所谓行为型,就包括:
“责任链”、“命令”、“解释器”、“迭代器”、“中介者”、“备忘录”、“观察者”、“状态”、“策略”、“模板方法”、“访问者”、“空对象”
(三)所谓结构型,就包括:
“适配器”、“桥接”、“组合”、“装饰”、“外观”、“享元”、“代理”
一、单例模式
一般情况下,单例模式中都有:
1、一个私有静态变量放实例变量
2、一个私有构造函数
3、一个公有静态实例对象返回方法getInstance()
单例模式具有延迟加载、公用一个实例对象,节约资源,提高性能的特点。
① 首先介绍第一种 “懒汉式---线程不安全” 的单例模式(甚至都不能被称为单例)
解释都在代码注释里:
public class Singleton {
//一个私有静态变量:类加载时载入
private static Singleton singleton;
//一个私有构造函数
private Singleton(){}
//一个公有静态方法:用来返回创建的实例
public static Singleton getInstance(){
/*
这里只有lazy1 == null ,如果多个线程同时进入,
多个线程的都没有被实例化,那么将会导致实例化多个对象
造成线程不安全
*/
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
②于是出现了第二种:“饿汉式--线程安全”
只是将静态私有实例变量直接实例化,如:
private static Singleton singleton = new Singleton();
很显然这样并没有起到单例模式节约资源的作用(我每次getInatance都会重新实例化一个对象,还不如不写这个“单例模式”)
③继续改善,出现“懒汉式---线程安全”的单例模式
依旧采用“懒汉式”,也就是说依旧只是判断了 singleton == null ,只不过这里在getInstace方法上加了一个同步锁,如代码:
public static synchronized Singleton getInstance(){
这样做的好处可以是不同线程获取同一个实例对象,但是缺点也显而易见,那就是当多个线程竞争获取这个实例对象时候,会造成阻塞,你想要这个对象,我也想要。所以这种设计很容易导致性能问题。
④ 然后,然后就是“双重校验锁---线程安全”
通过对getInstace方法内部的实例化那一个部分,通过同步代码块的方法,在同步代码块外层和内层都加上之前的singleton == null,双重判断,使得线程安全,同时也尽可能的避免了锁占资源而导致的性能问题。
同时私有静态实例变量还要加一个“volatile ” 关键字
如:
private static volatile Singleton singleton = new Singleton();
getInstace方法代码修改如下:
public static Singleton getInstance(){
/*
双重校验,同步代码块,高效利用锁资源,避免线程安全,双重校验实现单例
*/
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
*: 加两层 if 的原因:
如果多个线程同时进入,如果只有一层if ,那么多个线程同样还是会“先后依次”进行实例化,这样也就违背了单例的原则。
加volatile的原因:
singleton = new Singleton();时,在jvm底层会有三个执行会进行:
1、给singleton对象分配内存空间
2、初始化对象
3、将对象指向分配的内存空间
由于JVM具有“指令重排”的特性,有可能这三个执行顺序会改变,比如 1>3>2,单线程下问题不大,但是多线程就会导致第二个线程判断对象不为空,返回对象,但是实际上这个对象是没有被初始化的。
:而volatile就是禁止JVM指令重排。
⑤ 上面的设计,除了“双重校验---线程安全”常用以外,一般其他不推荐使用,当然除了双重校验,还有:“静态内部类”、“枚举类”实现单例模式:
如下代码解释:
1、静态内部类
public class Singleton {
//一个私有构造函数
private Singleton(){}
//一个私有静态内部类:返回被final 关键字修饰的 实例对象
//静态内部类在类加载之后并不加载,只是在 getInstance 后才加载实例化
private static class SingletonMake{
private static final Singleton SINGLETON = new Singleton();
}
//一个公有静态方法:用来返回创建的实例
public static Singleton getInstance(){
return SingletonMake.SINGLETON;
}
}
2、枚举类(这是最受欢迎的单例设计)
原因: 代码量少、简洁、虚拟机底层自行解决线程安全问题、可以避免反射攻击(“双重校验锁” 其实存在这个问题)
public enum Singleton {
INSTANCE;
//该类必要的对象
private String name;
//后面写该类的一些方法,比如:
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
//测试
public static void main(String[] args) {
//实例化一个枚举类对象
Singleton oneSingleton = Singleton.INSTANCE;
//执行更新操作
oneSingleton.setName("one");
System.out.println("oneSingleton.getName() :"+oneSingleton.getName());
Singleton twoSingleton = Singleton.INSTANCE;
System.out.println("oneSingleton == twoSingleton :" +(oneSingleton == twoSingleton));
}
}
/********************************************************/
/**运行结果:
oneSingleton.getName() :one
oneSingleton == twoSingleton :true
*/
网友评论