目录
以前设计模式看过很多遍,当时看会了,过了一个星期就又忘记了。现在重新整理一次,内容不仅简单,而且写的很详细。看了这篇内容,你有不一样的收获。
单例模式应用场景
J2EE中的ServletContext、ServletContextConfig 等、Spring 框架中的ApplicationContext、数据库连接池都是单例形式。
java中单例模式有以下特点:
1、单例类只能有一个实例。(不允许被其他类new,所以构造方法必须是私有private )
2、单例类必须自己自己创建自己的唯一实例。(单例类自己new一次)
3、单例类必须给所有其他对象提供这一实例。(提供给其他类使用,所以实例方法是 static静态类型,可以通过 类名.方法名 调用)
一、饿汉模式,线程安全
/**
* 优点:执行效率高,性能高,没有任何的锁
* 缺点:某些情况下,可能会造成内存浪费
*/
public class HungrySingleton {
//1.构造方法私有(防止其他类通过new 实例类)
private HungrySingleton() {
}
//2.getInstance方法是static 静态类型,引入的属性也需是static; final 实例被创建后不能修改,此处加上关键字final
private static final HungrySingleton hungrySingleton = new HungrySingleton();
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
构造方法为什么是私有,上面的代码注释已经解释
private HungrySingleton() {}
hungrySingleton 为什么是static 和 final ,上面的代码注释已经解释 ;如果不加static 会有下图提示
private static final HungrySingleton hungrySingleton = new HungrySingleton();
二、懒汉模式
同步锁
package com.yab.lazy;
/**
* 优点:节省了内存,线程安全
* 缺点:性能低(在线程数量较多的情况下,如果CPU分配压力上升,会导致大批线程阻塞,导致程序性能下降。既保证线程安全,又提升性能的单例--双重检查锁)
*/
public class LazySimpleSingletion {
//1.构造方法私有(防止其他类通过new 实例类)
private LazySimpleSingletion() {
}
//2.getInstance 方法是static 静态类型,引入的属性也需是static;
private static LazySimpleSingletion instance;
//3.synchronized 保证在多线程情况下,线程安全
public synchronized static LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
synchronized 保证在多线程情况下,线程安全
public synchronized static LazySimpleSingletion getInstance()
synchronized 总说能保证线程安全,怎么保证对象只有一个实例。
下面举个例子,如果不加 synchronized 会有什么结果呢,把代码稍有改动,去掉synchronized ;再加一个测试类
package com.yab.lazy;
/**
* 优点:节省了内存,线程安全
* 缺点:性能低(在线程数量较多的情况下,如果CPU分配压力上升,会导致大批线程阻塞,导致程序性能下降。既保证线程安全,又提升性能的单例--双重检查锁)
*/
public class LazySimpleSingletion {
//1.构造方法私有(防止其他类通过new 实例类)
private LazySimpleSingletion() {
}
//2.getInstance 方法是static 静态类型,引入的属性也需是static;
private static LazySimpleSingletion instance;
//3.synchronized 保证在多线程情况下,线程安全
public static LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
package com.yab.lazy.test;
import com.yab.lazy.LazySimpleSingletion;
public class ExectorThread implements Runnable{
@Override
public void run() {
LazySimpleSingletion instance = LazySimpleSingletion.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}
public static void main(String[] args) {
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("End");
}
}
此时不加 synchronized ,多次运行之后,有概率出现两个结果。
双重检查锁
package com.yab.lazy;
/**
* 双重检查锁
* 优点:性能高了,线程安全了
* 缺点:可读性难度加大
*/
public class LazyDoubleCheckSingleton {
//1.构造方法私有(防止其他类通过new 实例类)
private LazyDoubleCheckSingleton(){}
private volatile static LazyDoubleCheckSingleton instance;
public static LazyDoubleCheckSingleton getInstance(){
//检查是否要阻塞
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
//检查是否要重新创建实例
if (instance == null) {
instance = new LazyDoubleCheckSingleton();
//指令重排序的问题
}
}
}
return instance;
}
}
静态内部类
package com.yab.lazy;
/**
* 优点:写法优雅,利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
* 缺点:不优雅
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){
//此处防止通过反射破坏单例
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");
}
}
public static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
演示通过反射破坏单例
把此处三行代码注释后,会有什么效果呢
//此处防止通过反射破坏单例 if(LazyHolder.INSTANCE != null){ throw new RuntimeException("不允许非法访问"); }
关键代码
package com.yab.lazy;
/**
* 优点:写法优雅,利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
* 缺点:不优雅
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){
//此处防止通过反射破坏单例
// if(LazyHolder.INSTANCE != null){
// throw new RuntimeException("不允许非法访问");
// }
}
public static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
测试代码
package com.yab.lazy.test;
import com.yab.lazy.LazyStaticInnerClassSingleton;
import java.awt.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class LazyStaticInnerClassSingletonTest {
public static void main(String[] args) {
try {
Class clazz = LazyStaticInnerClassSingleton.class;
//通过反射获取私有的构造方法
Constructor constructor=clazz.getDeclaredConstructor(null);
//强制访问,强吻
constructor.setAccessible(true);
//第一次暴力初始化
Object obj1 = constructor.newInstance();
//第二次暴力初始化,相当于new 了两次
Object obj2 = constructor.newInstance();
System.out.println(obj1 == obj2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果如下,创建了两个不同的实例
此时此刻,把下面代码加上
//此处防止通过反射破坏单例 if(LazyHolder.INSTANCE != null){ throw new RuntimeException("不允许非法访问"); }
运行测试代码,结果如下,对象不会被实例
三、序列化单例
package com.yab.seriable;
import java.io.Serializable;
public class SeriableSingleton implements Serializable {
//序列化
//把内存中对象的状态转换为字节码的形式
//把字节码通过IO输出流,写到磁盘上
//永久保存下来,持久化
//反序列化
//将持久化的字节码内容,通过IO输入流读到内存中来
//转化成一个Java对象
public final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance(){
return INSTANCE;
}
private Object readResolve(){ return INSTANCE;}
}
四、枚举式单例
package com.yab.register;
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){return INSTANCE;}
}
五、容器式单例
package com.yab.register;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getInstance(String className){
Object instance = null;
if(!ioc.containsKey(className)){
try {
instance = Class.forName(className).newInstance();
ioc.put(className, instance);
}catch (Exception e){
e.printStackTrace();
}
return instance;
}else{
return ioc.get(className);
}
}
}
六、线程单例
package com.yab.threadlocal;
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocaLInstance.get();
}
}