01-(创)设计模式:单例模式
2021-05-30 本文已影响0人
XAbo
一、懒汉式
方式1:静态内部类【推荐】
优势:在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
public class Singleton {
// 因为JVM在加载外部类的过程中,是不会加载内部类的;
//只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。
// 私有化构造方法
private Singleton() {}
private static class SingletonHolder{
private static final Singleton INATANCE= new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.INATANCE;
}
方式2:成员属性,线程不安全
public class Singleton {
private static Singleton instance = null;
// 私有化构造方法
private Singleton() {}
// 方式二:多线程,当线程1还没创建时候,线程2先获取对象,导致创建两个对象。
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
方式3:成员属性,线程安全,低效率
public class Singleton {
private static Singleton instance = null;
// 私有化构造方法
private Singleton() {}
// 方式三: synchronized解决方式二缺点.
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
方式4:成员属性,线程安全,高效率
public class Singleton {
private volatile static Singleton instance = null;
// 私有化构造方法
private Singleton() {}
// 方式四: 双重判断,解决synchronized性能低.在多线程中可能出现空指针;
//原因:JVM在实例对象时候会进行指令优化和指令重排序。解决方案:volatile
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
二、饿汉式
方式一:正常类
public class Singleton {
// 方式一:静态代码块
/** static{
private static Singleton instance = new Singleton();
}
**/
// 方式二:静态成员属性
private static Singleton instance = new Singleton();
// 私有化构造方法
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
方式二:枚举类 【推荐】
枚举是线程安全的,只会加载一次,是唯一不会比破坏的单例模式。
public enum Singleton {
INATANCE;
public void todo(){
System.out.println("xzb");
}
}
Client:
public class Client {
public static void main(String[] args) {
Singleton instance0 = Singleton.INATANCE;
Singleton instance1 = Singleton.INATANCE;
System.out.println(instance0==instance1);
instance0.todo();
}
}
三、单例模式的破坏
破坏模式的方式:序列化和反射。
import java.io.Serializable;
public class Singleton implements Serializable {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
方式一:序列化
public class Client {
public static void main(String[] args) throws Exception {
//writeObject2File();
Singleton instance1 = readObjectFromFile();
Singleton instance2 = readObjectFromFile();
System.out.println(instance1==instance2);//false
}
//向文件中写数据
public static void writeObject2File() throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\a.txt"));
oos.writeObject(Singleton.getInstance());
oos.close();
}
//读取文件数据
public static Singleton readObjectFromFile() throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.txt"));
Singleton instance = (Singleton)ois.readObject();
ois.close();
return instance;
}
}
方式二:反射
import java.lang.reflect.Constructor;
public class Client {
public static void main(String[] args) throws Exception {
Class clazz = Singleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Singleton instance1 = (Singleton)cons.newInstance();
Singleton instance2 = (Singleton)cons.newInstance();
System.out.println(instance1==instance2);//false
}
}
三、单例模式的破坏的解决
一、序列化破坏的解决
在Singleton中添加readResolve()方法,在序列化时被反射调用,如果定义这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
import java.io.Serializable;
public class Singleton implements Serializable {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
public Object readResolve(){
return instance;
}
}
二、反射破坏的解决
并非解决,是阻止创建。
public class Singleton implements Serializable {
private static Singleton instance = new Singleton();
private static boolean flag =false;
private Singleton() {
synchronized (Singleton.class){
if(flag){
throw new RuntimeException("不能创建多个对象");
}
flag=true;
}
}
public static Singleton getInstance() {
return instance;
}
}
四、JDK中Runtime单例模式
public class Client {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("ipconfig");
InputStream is = process.getInputStream();
byte[] arr = new byte[1024 * 1024 *100];
int len = is.read(arr);
System.out.println(new String(arr,0,len,"GBK"));
}
}
结果