单例模式学习
- 作者: 花落季16238097
- 来源: 51数据库
- 2020-08-06
单例模式学习
1 饿汉式单例模式
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 饿汉式单例模式
public class Hungry {
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
}
- 还没用就创建了对象,可能会浪费空间
2 懒汉式单例模式
无线程锁
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懒汉式单例模式
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName() + "OK");
}
private static Lazy lazy = null;
public static Lazy getInstance()
{
if (lazy == null)
{
lazy = new Lazy();
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
{
new Thread(Lazy::getInstance).start();
}
}
}
- 单线程下可以,但是多线程不安全
加线程锁
package main.java.com.yuehun.singleton;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懒汉式单例模式
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName() + " OK");
}
private volatile static Lazy lazy = null;
// 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
public static Lazy getInstance()
{
if (lazy == null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy(); // 不是一个原子性操作
/**
* 1 分配内存空间
* 2 执行构造方法,初始化对象
* 3 把这个对象指向这个空间
*
* 可能会发生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
{
new Thread(Lazy::getInstance).start();
}
}
}
3 单例不是安全的
使用反射破坏单例模式
- Java 里面有个东西叫
反射
- 比如:
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 饿汉式单例模式
public class Hungry {
private Hungry(){}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
public static void main(String[] args) throws Exception {
Hungry instance = new Hungry();
Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Hungry instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
阻止反射破坏
阻止
反射
的破坏
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 饿汉式单例模式
public class Hungry {
private Hungry(){
synchronized (Hungry.class)
{
if (HUNGRY != null)
{
throw new RuntimeException("不要试图使用反射破坏单例模式");
}
}
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance()
{
return HUNGRY;
}
public static void main(String[] args) throws Exception {
Hungry instance = new Hungry();
Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Hungry instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
还有一种反射破坏
- 如果两个对象都是通过反射构造的,就不会触发异常
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懒汉式单例模式
public class Lazy {
// private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
// if (!flag)
// flag = true;
// else {
if (lazy != null) {
throw new RuntimeException("不要试图使用反射破坏单例模式");
// }
}
}
}
private volatile static Lazy lazy = null;
// 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一个原子性操作
/**
* 1 分配内存空间
* 2 执行构造方法,初始化对象
* 3 把这个对象指向这个空间
*
* 可能会发生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
又有一种解决方法
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懒汉式单例模式
public class Lazy {
private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
if (!flag)
flag = true;
else
throw new RuntimeException("不要试图使用反射破坏单例模式");
}
}
private volatile static Lazy lazy = null;
// 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一个原子性操作
/**
* 1 分配内存空间
* 2 执行构造方法,初始化对象
* 3 把这个对象指向这个空间
*
* 可能会发生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
破坏者又来了
- 可以通过反射获取到我们设置的这个标志位,在以后每一次创建对象的时候修改它
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
// 懒汉式单例模式
public class Lazy {
private static boolean flag = false;
private Lazy()
{
synchronized (Lazy.class)
{
if (!flag)
flag = true;
else
throw new RuntimeException("不要试图使用反射破坏单例模式");
}
}
private volatile static Lazy lazy = null;
// 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
public static Lazy getInstance()
{
if (lazy == null)
{
synchronized (Lazy.class)
{
if (lazy == null)
{
lazy = new Lazy(); // 不是一个原子性操作
/**
* 1 分配内存空间
* 2 执行构造方法,初始化对象
* 3 把这个对象指向这个空间
*
* 可能会发生指令重排!!!
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception
{
Field flag = Lazy.class.getDeclaredField("flag");
flag.setAccessible(true);
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy instance = declaredConstructor.newInstance();
flag.set(instance, false);
Lazy instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
道高一尺,魔高一丈
4 枚举解决
- enum 本身也是一个 Class 类
package main.java.com.yuehun.singleton;
import java.lang.reflect.Constructor;
/**
* main.java.com.yuehun.singleton
*
* @author yuehun
* Created on 2020/4/27.
*/
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance()
{
return INSTANCE;
}
}
class Test {
public static void main(String[] args) throws Exception {
EnumSingleton instance1 = EnumSingleton.INSTANCE;
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingleton instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
到此,阻止反射破坏就成功了!
原文链接:http://www.cnblogs.com/yuehungege/p/studynotes_singletonpatterns.html
推荐阅读