一、单例模式
1. 理解(理解可参考 模式1懒汉模式)
就是定义一个static的new 自己的成员变量
其他类想使用单例模式的类就只需要调用static 的成员变量
即可不用new 新的实例,只有1个实例(而且还是自己创建)
所以叫做单例模式。
2. 优点
1.节约内存:在一个内存中只使用一个实例,避免平凡创建实例和垃圾回收销毁实例的内存开销
2.防止对资源的多重占用.
3.缺点
1.没有接口,不能继承,
2.和单一职责冲突,一个类应该只关注实现的逻辑,而不是关心外面如何实例化
4. 使用场景
1.要求生产唯一的序列号(优点2)
2.web中的计数器,不用每次刷新就在数据库里面加一次,使用单例模式先缓存起来
3.创建对象需要消耗的资源很多,比如,IO和数据库的连接
5. 注意事项,同步安全
1.getInstance() 方法中需要使用同步锁 synchronized() 锁起来,防止多线程同时进入导致 instance 被多次实例化
二、6种单例模式的代码
提示:
1.一般不建议使用第1种和第2种懒汉模式
2.建议使用第3种饿汉模式
3.只有在明确要求 lazy loading 效果时候才会使用第5种模式
4.如果涉及到反序列化创建对象,使用6种枚举模式
5.如果有其他特殊的需求,可以考虑 4种双检锁模式
1. (不推荐)懒汉式(线程不安全)
功能 | 是否实现 |
---|---|
lazy | 是 |
多线程 | 否 |
难度 | 草鸡简单 |
描述 | 因为没有加锁,synchronize 所以不能多线程,严格来讲不叫单例模式,工作中不常用。 |
优点 | 简单 |
缺点 | 不安全,而且无多线程,在多线程时候无法真实地单例 |
1.SingleObject.java
/**
* 作用: 定义单例模式的
*/
public class SingleObject {
//1.单例模式唯一的实例.的声明.(使用private封装,public的get)
private static SingleObject instance ;
//2.构造函数 private 很关键,其他类无法实例化,只能单例模式。
private SingleObject() {
}
//3. get 唯一给外面的接口,就是 getInstance 实例化定义 并 返回实例.
public static SingleObject getInstance() {
//定义实例
if(instance == null){
instance = new SingleObject();
}
//返回实例
return instance;
}
//4. 可供实例调用的方法.
public void printSomeText() {
System.out.println("单例模式: 调用方法printSomeText()成功 ");
}
}
2.SingleObjectDemo.java
public class SingleObjectDemo {
public static void main(String[] args) {
//1. 通过单例模式唯一的getInstance()获取实例
SingleObject instance = SingleObject.getInstance();
//2. 使用获取的实例调用单例模式的方法》
instance.printSomeText();
}
}
Run:
单例模式: 调用方法printSomeText()成功
Process finished with exit code 0
2. (不推荐)单例模式(线程安全)
getInstance添加了一个线程安全锁synchronized
功能 | 是否实现 |
---|---|
lazy | 是 |
多线程 | 否 |
难度 | 简单 |
描述 | 具备很好的lazy loading 能在线程中很好的工作,但是效率低下,99%的情况需要不需要同步。 |
优点 | 第一次调用才初始化,避免内存浪费 |
缺点 | 必须加锁才能保证单例,加锁会影响效率,getInstance( )对性能影响不是很关键。 |
代码:
1.
区别于模式1懒汉模式
getInstance添加了一个线程安全锁synchronized
package 设计模式.单例模式.模式2安全懒汉模式_不推荐;
/**
* 作用: 定义单例模式的
* 区别于模式1懒汉模式
* getInstance添加了一个线程安全锁
* synchronized
*/
public class SingleObject {
//1.单例模式唯一的实例.的声明.(使用private封装,public的get)
private static SingleObject instance ;
//2.构造函数 private 很关键,其他类无法实例化,只能单例模式。
private SingleObject() {
}
//3. get 唯一给外面的接口,就是 getInstance 实例化定义 并 返回实例.
public static synchronized SingleObject getInstance() {
//定义实例
if(instance == null){
instance = new SingleObject();
}
//返回实例
return instance;
}
//4. 可供实例调用的方法.
public void printSomeText() {
System.out.println("单例模式: 调用方法printSomeText()成功 ");
}
}
3. 饿汉模式(常用)
new SingleObject() 写到成员变量里
功能 | 是否实现 |
---|---|
lazy | 否 |
多线程 | 是(靠classloader的装载,不用等到程序多线程使用所以避免多线程问题) |
难度 | 简单 |
描述 | 常用但是容易产生垃圾对象。 |
优点 | 没有加锁,效率高 |
缺点 | 类加载就初始化,浪费内存 |
注解 | 饿汉基于 classloader模式,避免的多线程的安全问题,不过instance在类装载的时候就会实例化,所以点点浪费内存 没达到 lazy loading的效果,但是一个装载也浪费不了多少内存所以最常用 |
代码:
3. 就是把
new SingleObject() 写到成员变量里
1.
/**
* 作用: 定义单例模式
*
* 3. 就是把
* new SingleObject() 写到成员变量里
*/
public class SingleObject {
//1.在类装载的时候就定义好了实例内容
private static SingleObject instance = new SingleObject();
private SingleObject() {
}
public static synchronized SingleObject getInstance() {
return instance;
}
public void printSomeText() {
System.out.println("单例模式: 调用方法printSomeText()成功 ");
}
}
4. 双检锁(double checked locking)
判断是否存在再进入线程锁再判断是否存在
功能 | 是否实现 |
---|---|
lazy | 是(和懒汉1,2一样在调用的时候才初始化) |
多线程 | 是 |
难度 | 复杂 |
优点 | 双检锁,多线程下能安全且高性能 |
缺点 | 复杂 |
使用场景 | getInstance()对程序的影响很大的时候 |
判断是否存在再进入线程锁再判断是否存在
/**
* 作用: 定义单例模式
*
* 4. 就是把
* 双检锁就是判断是否存在再进入线程锁再判断是否存在,
* 保证不会多创建实例
* 而且比较高效
*/
public class SingleObject {
private static SingleObject instance ;
private SingleObject() {
}
public static SingleObject getInstance() {
/**
* * 双检锁就是判断是否存在再进入线程锁再判断是否存在,
* * 保证不会多创建实例
* * 而且比较高效
*/
if (instance == null) {
synchronized (SingleObject.class){
if (instance == null){
instance = new SingleObject();
}
}
}
return instance;
}
public void printSomeText() {
System.out.println("单例模式: 调用方法printSomeText()成功 ");
}
}
5. 登记式(静态内部类)
5.定义静态内部类里成员变量初始化 实例
getInstance()返回静态内部类的静态成员变常量
功能 | 是否实现 |
---|---|
lazy | 是(和懒汉不同,这个在显示调用getInstance() 才会装载静态内部类) |
多线程 | 是(装载静态内部类的classLoader机制,适用于多线程) |
难度 | 复杂 |
使用场景 | 存在静态区域 |
描述 | 和双检锁一样的功效,安全且高效但是只适用于有静态域的情况,双检锁是getInstance() 时候实例化,登记类(静态内部类)是在显示调用时候装载静态static内部类StaticInnerClass{},完成的初始化。 |
代码:
1.
/**
* 作用: 定义单例模式
* 5.定义静态内部类里成员变量初始化 实例
* getInstance()返回静态内部类的静态成员变常量
*/
public class SingleObject {
//定义静态内部类里成员变量初始化 实例
private static class StaticInnerClass {
private static final SingleObject instance = new SingleObject();
}
private SingleObject() {
}
public static SingleObject getInstance() {
//返回静态内部类的静态成员变常量
return StaticInnerClass.instance;
}
public void printSomeText() {
System.out.println("单例模式: 调用方法printSomeText()成功 ");
}
}
6. 枚举
功能 | 是否实现 |
---|---|
lazy | 否 |
多线程 | 是(自动支持序列化机制,绝对防止被多次实例化) |
难度 | 简单,但是生疏 |
使用场景 | 不适用反射 reflection attack 的场景 |
优点 | (最佳方式但是不熟练) |
代码:
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}