设计模式之单例模式
Posted amcomputer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之单例模式相关的知识,希望对你有一定的参考价值。
1 背景回顾
设计模式是一种思维方式,前人总结的最佳实践,也是一种态度,一种进步。
软件过程里面设计模式一共有23种,一般分为3类。即创建型,结构性型,行为型。其中:
**创建型5种: ** 解决了创建对象的烦恼
单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式
结构性型7种: 解决了如何让类组合起来完成复杂的功能
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式,
**行为型11种:**解决了类之间的控制关系。
模版方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,状态模式,策略模式,职责链模式,访问者模式,解释器模式。
如果想要学习并掌握这些设计模式,最后是编码和画图理解。
2 单例模式
单例模式比较好理解,一个类只有一个instance。关键是利用类加载器和构造方法私有。具体实现方法一般有2种。
饿汉式:在内存的方法区先new一个对象,然后直接返回。
懒汉式:调用时在返回
双重检测机制:适合多线程场景
3 饿汉式
package com.yang.create.single;
}
public class Hungry{
private Hungry() {
}
private static final Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
public static void main(String[] args) {
Hungry instance = Hungry.getInstance();
Hungry instance2 = new Hungry();
Hungry instance3 = new Hungry();
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
System.out.println(instance3.hashCode());
}
}
类内输出结果:
发现不仅能输出不同的对象,还能使用new 来产生对象(思考:为什么已经构造器私有化了,还能new出对象,后面会解答)
类外或者包外输出结果:
分析:hashcode一样,在类外使用new关键字时报错。说明达到实验效果,需要注意的是类内依旧可以new,涉及到访问权限问题。
4 懒汉式
class Lazyman{
private Lazyman() {
}
private static Lazyman LAZYMAN =null;
public static Lazyman getInstance(){
if( LAZYMAN == null ){
LAZYMAN = new Lazyman();
}
return LAZYMAN;
}
}
多线程时,可能会不安全。我开启了200个线程(10个也试过),会导致线程开启失败。
思考:为什么前者是安全的,到了懒汉式,却不安全了?
因为少了final关键词,动态生成LAZYMAN
5 双重检测机制
package com.yang.create.single;
public class DoubleLock {
private DoubleLock() {
System.out.println(Thread.currentThread().getName() + "oK");
}
private static DoubleLock LAZYMAN = null;
public static DoubleLock getInstance() {
//双层检测
if (LAZYMAN == null) {
synchronized (LAZYMAN.getClass()) {//第一层 加锁
if (LAZYMAN == null) {//第2层
LAZYMAN = new DoubleLock(); //不是原子操作,第一步先分配内存,第二步执行构造方法,第3步指向内存空间
}
}
}
return LAZYMAN;
}
}
结果:
因为 LAZYMAN = new DoubleLock(); //不是原子操作,第一步先分配内存,第二步执行构造方法,第3步指向内存空间
所以导致异常,多线程下,3个步骤会被打乱。
所以需要加上防止指令重排操作:volatile
private volatile static DoubleLock LAZYMAN = null;
package com.yang.create.single;
public class DoubleLock {
private DoubleLock() {
System.out.println(Thread.currentThread().getName() + "oK");
}
private volatile static DoubleLock LAZYMAN = null;
public static DoubleLock getInstance() {
//双层检测
if (LAZYMAN == null) {
synchronized (DoubleLock.class) {//第一层 加锁
if (LAZYMAN == null) {//第2层
LAZYMAN = new DoubleLock(); //不是原子操作,第一步先分配内存,第二步执行构造方法,第3步指向内存空间
}
}
}
return LAZYMAN;
}
}
测试成功:
当然,如果使用反射,也会破坏单例模式。这里可以使用enum,即枚举类型来构造单例,enum是一个特殊的java类。
以上是关于设计模式之单例模式的主要内容,如果未能解决你的问题,请参考以下文章