常见单例模式写法规范以及实现原理分析
package com.cc.SingleTon.LazySingleTon;/**
* @Author chenduoduo
* @create 2020/7/21 21:25
* private static volatile LazySingleTon instance; 加上 volatile 防止 jvm指令重排
* 这些写的好处:
* 1、保证线程安全的问题
* 2、防止指令重排
* 3、双重加锁的优化
* 4、比较严密的写法
*/
public class LazySingleTon {
public static void main(String[] args) {
//多线程的情况保证单例模式的写法也很严密
new Thread(new Runnable() {
@Override
public void run() {
LazySingleTon lazySingleTon = LazySingleTon.getInstance();
System.out.println(lazySingleTon);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
LazySingleTon lazySingleTon = LazySingleTon.getInstance();
System.out.println(lazySingleTon);
}
}).start();
}
private LazySingleTon(){}
private static volatile LazySingleTon instance;
public static LazySingleTon getInstance() {
if(instance == null){
synchronized (LazySingleTon.class){
if(instance == null){
instance = new LazySingleTon();
}
}
}
return instance;
}
}
二、饿汉模式
保证线程安全是通过类加载机制完成
package com.cc.SingleTon.HungrySingleTon;/**
* @Author chenduoduo
* @create 2020/7/21 21:51
* 饿汉模式:在类加载的初始化的时候就创建实例对象
* 通过类加载机制保证饿汉模式的安全
* protected Class<?> loadClass(String name, boolean resolve)
* throws ClassNotFoundException
* {
* synchronized (getClassLoadingLock(name)) {
* // First, check if the class has already been loaded
* Class<?> c = findLoadedClass(name);
* if (c == null) {
* long t0 = System.nanoTime();
* try {
* if (parent != null) {
* c = parent.loadClass(name, false);
* } else {
* c = findBootstrapClassOrNull(name);
* }
* } catch (ClassNotFoundException e) {
* // ClassNotFoundException thrown if class not found
* // from the non-null parent class loader
* }
*
* if (c == null) {
* // If still not found, then invoke findClass in order
* // to find the class.
* long t1 = System.nanoTime();
* c = findClass(name);
*
* // this is the defining class loader; record the stats
* sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
* sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
* sun.misc.PerfCounter.getFindClasses().increment();
* }
* }
* if (resolve) {
* resolveClass(c);
* }
* return c;
* }
* }
*/
public class HungrySingleTon {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
HungrySingleTon hungrySingleTon = HungrySingleTon.getInstance();
System.out.println(hungrySingleTon);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
HungrySingleTon hungrySingleTon = HungrySingleTon.getInstance();
System.out.println(hungrySingleTon);
}
}).start();
}
private HungrySingleTon(){
}
private static HungrySingleTon instance = new HungrySingleTon();
public static HungrySingleTon getInstance(){
return instance;
}
}
三、枚举类型
通过反射攻击创建实例的时候 ,源码会显示以下错误
package com.cc.SingleTon.EnumSingleTon;/**
* @Author chenduoduo
* @create 2020/7/31 20:58
* 枚举单例模式
*/
public enum EnumSIngleTon {
INSTANCE;
public void print(){
System.out.println(this.hashCode());
}
}
Constructor<EnumSIngleTon> constructor = EnumSIngleTon.class.getDeclaredConstructor(String.class, int.class);constructor.setAccessible(true);EnumSIngleTon enumSIngleTon1 = constructor.newInstance("INSTANCE", 0);
if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
四、序列化实现实例化
反序列化创建实例可能会出现的问题:读出来的实例对象和直接获取的对象存在不一致的情况,不是同一个实例
举例如下:
package com.cc.SingleTon.serializabletest;import java.io.*;
/**
* @Author chenduoduo
* @create 2020/7/31 21:45
*/
public class TestSerializanle {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化:把HungrySingleTon 存到磁盘中
HungrySingleTon instance = HungrySingleTon.getInstance();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("instance"));
objectOutputStream.writeObject(instance);
objectOutputStream.close();
}
}
然后我们再把对象反序列化回来,把数据存在内存中,比较获取的两个对象结果显示如下
* 反序列会出现的问题:* Serializable直接实现接口的话,会默认生成* he serialization runtime will calculate a default serialVersionUID** 会出现的问题:在序列化对象里面添加 其他属性的时候,例如 String name 会报错* 解决的办法最好把序列化指定的ID加上
package com.cc.SingleTon.serializabletest;import java.io.*;
/**
* @Author chenduoduo
* @create 2020/7/31 21:45
*/
public class TestSerializanle {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("instance"));
Object object = objectInputStream.readObject();
objectInputStream.close();
HungrySingleTon hungrySingleTon1 = ((HungrySingleTon)object);
HungrySingleTon hungrySingleTon2 = HungrySingleTon.getInstance();
System.out.println(hungrySingleTon1 == hungrySingleTon2);
}
}
处理方法在序列化对象中添加一个默认的签名的方法:Serializable 默认提供了解决方法
package com.cc.SingleTon.serializabletest;import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 反序列会出现的问题:
* Serializable直接实现接口的话,会默认生成
* he serialization runtime will calculate a default serialVersionUID
*
* 会出现的问题:在序列化对象里面添加 其他属性的时候,例如 String name 会报错
* 解决的办法最好把序列化指定的ID加上
*/
public class HungrySingleTon implements Serializable {
static final long serialVersionUID = 5363798507173947426L;
private HungrySingleTon(){
}
private static HungrySingleTon instance = new HungrySingleTon();
public static HungrySingleTon getInstance(){
return instance;
}
//提供一个指定签名的方法来读取数据流的问题
Object writeReplace() throws ObjectStreamException {
return instance;
}
}
以上是 常见单例模式写法规范以及实现原理分析 的全部内容, 来源链接: utcz.com/z/518956.html