什么是单例
保证一个虚拟机中的类中只有一个实例
加载类型
立即加载:项目启动就加载
懒加载:需要使用到的时候才加载
static修饰的变量或代码块为项目启动就加载,且只会加载一次
单例的七种模式
饿汉式 项目启动就立即加载导致项目启动慢
/**
* 饿汉式
* 优点:线程安全
* 缺点:项目加载将实例化单例,占用内存资源,项目加载慢
* @author administrator
*/
public class UnLazySingleton {
private UnLazySingleton() {
}
private static UnLazySingleton unLazySingleton = new UnLazySingleton();
public static UnLazySingleton getSingleton(){
return unLazySingleton;
}
public static void main(String[] args) {
System.out.println(UnLazySingleton.getSingleton());
System.out.println(UnLazySingleton.getSingleton());
}
}
懒汉式 线程不安全 :创建对象包含创建(写入内存)和赋值(读取内存对象),获取对象是线程安全的只涉及读操作
一个线程是否安全依据–要么读要么写
/**
* 懒汉式
* 优点:需要使用再加载
* 缺点:线程不安全
*
* @author administrator
*/
public class LazySingleton {
public LazySingleton() {
}
private static LazySingleton lazySingleton = null;
public static LazySingleton getSingleton() {
if (lazySingleton == null) {
//模拟多线程出现线程安全问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
public static void main(String[] args) {
// System.out.println(LazySingleton.getSingleton());
// System.out.println(LazySingleton.getSingleton());
//模拟多线程出现线程安全问题,需要注释上面代码
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(LazySingleton.getSingleton());
}
}).start();
}
}
}
懒汉式 线程安全 多线程时效率不高,因获取对象只涉及读操作,线程是安全的,没必要加锁
/**
* 懒汉式
* 优点:需要使用再加载
* 缺点:线程安全,多线程时效率低,因为在创建对象和获取对象的时候都加了锁,获取对象的时候可以不加锁
*
* @author administrator
*/
public class LazySafeSingleton {
private LazySafeSingleton() {
}
private static LazySafeSingleton lazySingleton = null;
public synchronized static LazySafeSingleton getSingleton() {
if (lazySingleton == null) {
lazySingleton = new LazySafeSingleton();
}
return lazySingleton;
}
public static void main(String[] args) {
System.out.println(LazySingleton.getSingleton());
System.out.println(LazySingleton.getSingleton());
}
}
DCL双检查锁 线程安全 多线程时初始化对象效率低,获取对象效率高,因初始化时加了锁
/**
* double check lock 双检查锁 解决线程安全和效率问题,但是第一次加载慢
*
* @author administrator
*/
public class DCLSingleton {
private DCLSingleton() {
}
//volatile保证可见性和指令重排
private volatile static DCLSingleton lazySingleton = null;
public static DCLSingleton getSingleton() {
if (lazySingleton == null) {
//创建对象包含创建(写入内存)和赋值(读取内存对象)非原子操作存在线程安全,需要加锁 (原子操作:要么读要么写)
synchronized (DCLSingleton.class) {
//防止多线程重复创建,导致为非单例对象
if (lazySingleton == null) {
lazySingleton = new DCLSingleton();
}
}
}
//返回只包含读操作,线程是安全的,没必要加锁
return lazySingleton;
}
public static void main(String[] args) {
System.out.println(LazySingleton.getSingleton());
System.out.println(LazySingleton.getSingleton());
}
}
内部类,线程安全效率高 但是存在反射和序列化问题
/**
* 内部类 解决DCL第一次加载效率问题,但是存在被反射和序列化的问题
*
* @author administrator
*/
public class InnerSingleton {
private InnerSingleton() {
System.out.println("初始化构造方法");
}
public static InnerSingleton getSingleton() {
return InnerClass.innerSingleton;
}
private static class InnerClass {
//只初始化一次(static修饰的),保证线程安全和效率
private final static InnerSingleton innerSingleton = new InnerSingleton();
}
public static void main(String[] args) {
System.out.println(InnerSingleton.getSingleton());
System.out.println(InnerSingleton.getSingleton());
}
}
枚举 线程安全,效率高,解决反射和序列化 不能反射和序列化原因
public enum EnumSingleton {
INSTANCE;
// 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
public void add() {
System.out.println("add方法...");
}
public static void main(String[] args) throws Exception {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
EnumSingleton instance2 = EnumSingleton.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
容器管理
/**
* 使用容器管理单例
*
* @author administrator
*/
public class ContainManagerSingleton {
private static Map objMap = new ConcurrentHashMap<>();
public static void registerService(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
public static Object getService(String key) {
{
return objMap.get(key);
}
}
}
使用反射攻击单例
/**
* 反射攻击单例
*
* @author administrator
*/
public class AttackByReflectSingleton {
private AttackByReflectSingleton() throws Exception {
//解决反射破解单例
if (attackByReflectSingleton != null) {
throw new Exception("报错了");
}
}
private static AttackByReflectSingleton attackByReflectSingleton = null;
public static AttackByReflectSingleton getSingleton() throws Exception {
if (attackByReflectSingleton == null) {
attackByReflectSingleton = new AttackByReflectSingleton();
}
return attackByReflectSingleton;
}
public static void main(String[] args) throws Exception {
AttackByReflectSingleton singleton = AttackByReflectSingleton.getSingleton();
//使用反射破解单例模式,模拟攻击将解决方案注释掉就行
Constructor constructor = AttackByReflectSingleton.class.getDeclaredConstructor();
//设置可访问私有化方法
constructor.setAccessible(true);
AttackByReflectSingleton attackByReflectSingleton = constructor.newInstance();
//获取到不同对象表示被破解了
System.out.println(singleton);
System.out.println(attackByReflectSingleton);
}
}
通过序列化攻击单例
/**
* 序列化攻击单例
*/
public class AttackBySerializableSingleton implements Serializable {
private AttackBySerializableSingleton() {
}
//序列化:内存到硬盘
//反序列化:硬盘到内存
private static AttackBySerializableSingleton attackBySerializableSingleton = new AttackBySerializableSingleton();
public static AttackBySerializableSingleton getSingleton() {
return attackBySerializableSingleton;
}
public static void main(String[] args) throws Exception {
AttackBySerializableSingleton singleton = AttackBySerializableSingleton.getSingleton();
FileOutputStream fos = new FileOutputStream("F:\\code\\singleton.Object");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singleton);
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("F:\\code\\singleton.Object");
ObjectInputStream ois = new ObjectInputStream(fis);
Object singleton2 = ois.readObject();
ois.close();
fis.close();
System.out.println(singleton);
System.out.println(singleton2);
}
//返回序列化获取对象 ,保证为单例,解决序列化破解单例
public Object readResolve() {
return attackBySerializableSingleton;
}
}
思想来源