单例模式
Posted 醉梦了红尘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。
定义
单例模式,是为了在任何情况下都绝对只能有一个实例,并且提供一个全局访问点。单例模式属于创造型模式。
饿汉式单例模式
1.使用private修饰的静态成员属性直接初始化,利用jvm类加载机制实现单例
2.构造函数私有化,防止外部new
3.提供全局入口,获取单例实例
public class HugrySingleTon {
//private修饰静态成员属性初始化 利用jvm的类加载机制实现单例
private static final HugrySingleTon hugrySingleTon = new HugrySingleTon();
//构造函数私有化,防止外部调用 无法避免反射破坏
private HugrySingleTon() {
}
//提供全局入口获取单例实例
public static HugrySingleTon getHugryInstance(){
return HugrySingleTon.hugrySingleTon;
}
}
静态代码块实现方式
public class HugrySingleTon {
//private修饰静态成员属性初始化 利用jvm的类加载机制实现单例
private static final HugrySingleTon hugrySingleTon;
static {
hugrySingleTon = new HugrySingleTon()
}
//构造函数私有化,防止外部调用 无法避免反射破坏
private HugrySingleTon() {
}
//提供全局入口获取单例实例
public static HugrySingleTon getHugryInstance(){
return HugrySingleTon.hugrySingleTon;
}
}
优点: 写法简单,容易理解,能够保证线程安全,执行效率高,适用于单例对象较少的情况
缺点: 所有对象在容器初始化的时候完成初始化,会降低容器的启动速度,如果容器中存在大量的单例对象,还会对容器的内存造成较大的浪费。
懒汉式单例
为了解决饿汉式带来的内存浪费问题,出现了懒汉式单例的写法。
不加锁
public class LazySingleTon {
private static LazySingleTon lazySingleTon;
private LazySingleTon(){}
public static LazySingleTon getInstance(){
if(lazySingleTon == null) {
lazySingleTon = new LazySingleTon();
}
return lazySingleTon;
}
}
问题: 线程不安全,在多线程情况下,多个线程同时进入if方法同时满足条件,导致每次都创建了新的对象,违反了单例的定义
外层加锁
public class LazySingleTon {
private static LazySingleTon lazySingleTon;
private LazySingleTon() {
}
public static LazySingleTon getInstance() {
synchronized (LazySingleTon.class) {
if (lazySingleTon == null) {
lazySingleTon = new LazySingleTon();
}
return lazySingleTon;
}
}
}
问题: 线程安全,但是如果已经创建了对象,进去直接加锁,导致无用的加锁竞争,会浪费cpu资源
双重校验锁
public class LazySingleTon {
private static volatile LazySingleTon lazySingleTon;
private LazySingleTon() {
}
public static LazySingleTon getInstance() {
if(lazySingleTon != null){
return lazySingleTon;
}
synchronized (LazySingleTon.class) {
if (lazySingleTon == null) {
lazySingleTon = new LazySingleTon();
}
return lazySingleTon;
}
}
}
注意需要加volatile关键字防止指令重排序,导致DCL问题,拿到不完整的实例
破坏单例
反射破坏单例
直接通过反射拿到私有构造,获取对象,会破坏单例,newInstance本质上会调用无参构造,因此需要在无参构造函数中加入判断
public class HugrySingleTon {
//private修饰静态成员属性初始化 利用jvm的类加载机制实现单例
private static final HugrySingleTon hugrySingleTon = new HugrySingleTon();
//构造函数私有化,防止外部调用 无法避免反射破坏
private HugrySingleTon() {
if(hugrySingleTon != null) {
throw new RuntimeException("实例已存在!");
}
}
//提供全局入口获取单例实例
public static HugrySingleTon getHugryInstance(){
return HugrySingleTon.hugrySingleTon;
}
public static void main(String[] args) throws Exception {
HugrySingleTon hugryInstance1 = HugrySingleTon.getHugryInstance();
HugrySingleTon hugryInstance2 = HugrySingleTon.getHugryInstance();
System.out.println(hugryInstance1 == hugryInstance2);
System.out.println("************************************************");
Constructor<HugrySingleTon> constructor = HugrySingleTon.class.getDeclaredConstructor();
HugrySingleTon hugrySingleTon3 = constructor.newInstance();
System.out.println(hugryInstance1 == hugrySingleTon3);
}
}
序列化破坏单例
public class HugrySingleTon implements Serializable{
//private修饰静态成员属性初始化 利用jvm的类加载机制实现单例
private static final HugrySingleTon hugrySingleTon = new HugrySingleTon();
//构造函数私有化,防止外部调用 无法避免反射破坏
private HugrySingleTon() {
if(hugrySingleTon != null) {
throw new RuntimeException("实例已存在!");
}
}
//提供全局入口获取单例实例
public static HugrySingleTon getHugryInstance(){
return HugrySingleTon.hugrySingleTon;
}
/**
* 放开readResolve方法可以防止序列化破坏单例
private Object readResolve(){
return hugrySingleTon;
}
**/
public static void main(String[] args) throws Exception {
HugrySingleTon hugryInstance1 = HugrySingleTon.getHugryInstance();
HugrySingleTon hugryInstance2 = HugrySingleTon.getHugryInstance();
System.out.println(hugryInstance1 == hugryInstance2);
System.out.println("************************************************");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialized.obj"));
out.writeObject(hugryInstance1);
out.flush();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialized.obj"));
HugrySingleTon hugryInstance3 = (HugrySingleTon) in.readObject();
System.out.println(hugryInstance1 == hugryInstance3);
out.close();
in.close();
}
}
执行结果
源码分析
//java.io.ObjectInputStream#readObject(java.lang.Class<?>)
private final Object readObject(Class<?> type)
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
if (! (type == Object.class || type == String.class))
throw new AssertionError("internal error");
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(type, false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
...
try {
switch (tc) {
case TC_OBJECT:
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
}
}
...
}
private Object readOrdinaryObject(boolean unshared)
throws IOException
{...
Object obj;
try {
//实例化对象 desc是ObjectStreamClass newInstance不会走无参构造所以靠无参构造函数无法实现防止单例破坏
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
...
if (obj != null &&
handles.lookupException(passHandle) == null &&
//判断对象是否有readResovle的无参方法
desc.hasReadResolveMethod())
{
//执行readResovle方法,将返回值作为序列化结果
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
通过在序列化对象中增加
private Object readResolve(){
//返回原始单例实例
return hugrySingleTon;
}
通过程序判断是否有readResovle,将序列化的结果,强制替换为了原始单例对象,实际上还是创建了新的对象只是没有返回。浪费了内存
注册式单例
将每一个实例都登记在某一个地方,使用唯一标识来获取实例。
枚举式单例
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;
}
}
public class EnumSingletonTest {
public static void main(String[] args) throws Exception{
EnumSingleton originInstatnce = EnumSingleton.getInstance();
originInstatnce.setData(new Object());
FileOutputStream fo = new FileOutputStream("serialized.obj");
ObjectOutputStream oos = new ObjectOutputStream(fo);
oos.writeObject(originInstatnce);
oos.flush();
FileInputStream fi = new FileInputStream("serialized.obj");
ObjectInputStream ois = new ObjectInputStream(fi);
EnumSingleton serializedInstatnce = (EnumSingleton) ois.readObject();
fo.close();
oos.close();
fi.close();
ois.close();
System.out.println(originInstatnce == serializedInstatnce);
System.out.println(originInstatnce.getData() == originInstatnce.getData());
}
}
枚举式单例,是饿汉式单例的一种实现
枚举式单例无法被序列化破坏的原因:
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_ENUM:
if (type == String.class) {
throw new ClassCastException("Cannot cast an enum to java.lang.String");
}
return checkResolve(readEnum(unshared));
...
}
private Enum<?> readEnum(boolean unshared) throws IOException {
if (bin.readByte() != TC_ENUM) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
}
int enumHandle = handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(enumHandle, resolveEx);
}
String name = readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
try {
@SuppressWarnings("unchecked")
//通过Enum.valueOf方法 直接从类名和类对象从枚举中提取对应的枚举对象
//不存在通过newInstance多次构造的情况
Enum<?> en = Enum.valueOf((Class)cl, name);
result = en;
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name + " does not exist in " +
cl).initCause(ex);
}
if (!unshared) {
handles.setObject(enumHandle, result);
}
}
handles.finish(enumHandle);
passHandle = enumHandle;
return result;
}
枚举防止反射单例破坏
枚举的源码只提供了有参构造,并且在有参构造中做了单例的判断逻辑
容器式单例
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<>();
public static Object getBean(String className) {
synchronized (ioc) {
Object obj = null;
if(!ioc.containsKey(className)) {
try{
obj = Class.forName(className);
ioc.put(className, obj);
}catch (Exception e){
e.printStackTrace();
}
}else{
obj = ioc.get(className);
}
return obj;
}
}
}
ThreadLocal
线程安全的缓存对象,ThreadLocal只是作为操作线程对象的入口,包括设置初始值,get() ,set(),
实质上,每个Thread对象都维护这自己的ThreadLocalMap的成员属性,我们对象ThreadLocal的操作,实质上是对正在执行的线程的ThreadLocalMap的get和set,所以先对ThreadLocal的操作是线程安全的
以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章