单例模式定义:
确保一个类在任何情况下绝对只有一个实例,并提供全局访问点
单例模式的种类:
1.基本单例模式
(1)饿汉式:
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
优点:执行效率高,性能高,没有任何的锁
缺点:某些情况下,可能会造成内存浪费
(2)懒汉式:
public class LazySimpleSingletion {
private static LazySimpleSingletion instance;
private LazySimpleSingletion(){}
public synchronized static LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
懒汉式这里加了锁(synchronized ),不加锁的话会有线程问题
* 优点:节省了内存,线程安全
* 缺点:性能低
(3)懒汉式的优化:
对于性能这块我们使用的是双重校验的方式:
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
//检查是否要阻塞
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
//检查是否要重新创建实例
if (instance == null) {
instance = new LazyDoubleCheckSingleton();
//指令重排序的问题
}
}
}
return instance;
}
}
//指令重排序的问题:所以我们加了一个关键字 volatile;
我们还有更优雅的写法,可读性会更强:
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){
//这一步是为了防止反射进行破坏
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");
}
}
private static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
同时要小心序列化造成的破坏:
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;}
}
这个不够优雅,所以有了下面的枚举单例
2.注册单例模式
(1)枚举式单例模式
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;}
}
枚举底层就包含防反射破坏的判断,但是虽然优雅,同时也会造成内存的浪费。
所以不适合大量创建,这个时候就出现了spring的容器式单例
(2)容器式单例模式:
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);
}
}
}
但是这个就出现了线程安全的问题,只需要加锁即可
3.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();
}
}
线程单例模式就是在这个线程里他是单例的。
本文详细介绍了Java中的单例模式实现方式,包括饿汉式、懒汉式、双重校验锁(DCL)优化及静态内部类实现。此外,还探讨了枚举单例模式的安全性和内存消耗,以及序列化可能导致的单例破坏问题。最后提到了Spring容器式单例和ThreadLocal单例模式,分析了它们的适用场景和优缺点。
1385

被折叠的 条评论
为什么被折叠?



