单例模式
一、概述
单例模式的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。属于设计模式三大类中的创建型模式。 单例模式具有典型的三个特点:
-
只有一个实例。
-
自我实例化。
-
提供全局访问点。
二、优缺点
-
优点:由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。
-
缺点:也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难。
三、示例
饿汉式
//单例的思想: 构造器私有
public class SingletonDemo1 {
private SingletonDemo1(){
}
// 但容量浪费空间
private final static SingletonDemo1 singletonDemo1 = new SingletonDemo1();
public static SingletonDemo1 getInstance(){
return singletonDemo1;
}
}
懒汉式
package demo.singleton;
//在多线程下会失效
public class SingletonDemo2 {
private SingletonDemo2(){
System.out.println(Thread.currentThread().getName() + "111");
}
private static SingletonDemo2 singletonDemo2;
public static SingletonDemo2 getInstance(){
if (singletonDemo2 == null){
singletonDemo2 = new SingletonDemo2();
}
return singletonDemo2;
}
public static void main(String[] args) {
// 多线程下单例失效
for (int i = 0; i < 10; i++) {
new Thread(()->{
SingletonDemo2.getInstance();
}).start();
}
}
}
Thread-1111
Thread-2111
Thread-0111
懒汉式(双重检测锁式)
package demo.singleton;
// java创建对象总分为三步
// 1.分配内存空间
// 2.执行构造函数,创建对象
// 3.将对象指向空间
public class SingletonDemo3 {
private SingletonDemo3(){
System.out.println(Thread.currentThread().getName() + "111");
}
private static volatile SingletonDemo3 singletonDemo3;
public static SingletonDemo3 getInstance(){
if (singletonDemo3 == null){
// 细粒度加锁
synchronized (SingletonDemo3.class){
if (singletonDemo3 == null){
singletonDemo3 = new SingletonDemo3();
}
}
}
return singletonDemo3;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
SingletonDemo3.getInstance();
}).start();
}
}
}
思考为什么需要加volatile?
java创建对象总分为三步
1.分配内存空间
2.执行构造函数,创建对象
3.将对象指向空间
在多线程模式下,因为指令重排A线程进来先执行的13,对象并没有真正的被创建出来,但B线程此时进来了,发现对象已经存在,此时B线程拿到的对象就不是完整的,而volatile就是为了防止指令重排。
思考:双重检测锁式真的是安全的吗?答案是不安全的,可以通过反射去破坏
示例:
package demo.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SingletonDemo3 {
private SingletonDemo3(){
}
private static volatile SingletonDemo3 singletonDemo3;
public static SingletonDemo3 getInstance(){
if (singletonDemo3 == null){
// 细粒度加锁
synchronized (SingletonDemo3.class){
if (singletonDemo3 == null){
singletonDemo3 = new SingletonDemo3();
}
}
}
return singletonDemo3;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//通过反射创建对象
Constructor<SingletonDemo3> declaredConstructor = SingletonDemo3.class.getDeclaredConstructor();
SingletonDemo3 singletonDemo3 = declaredConstructor.newInstance();
SingletonDemo3 singletonDemo = declaredConstructor.newInstance();
System.out.println(singletonDemo.hashCode());
System.out.println(singletonDemo3.hashCode());
}
}
结果:
21685669
2133927002
由此可以看出两个对象的hashCode是不一样的,所以不安全;
怎么解决?==静态内部类式==
静态内部类式
package demo.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SingletonDemo4 {
private static boolean flag = false;
private SingletonDemo4(){
synchronized (SingletonDemo4.class){
if (!flag){
flag = true;
}else {
throw new RuntimeException("不要破坏单例");
}
}
}
private static volatile SingletonDemo4 singletonDemo4;
public static SingletonDemo4 getInstance(){
if (singletonDemo4 == null){
// 细粒度加锁
synchronized (SingletonDemo4.class){
if (singletonDemo4 == null){
singletonDemo4 = new SingletonDemo4();
}
}
}
return singletonDemo4;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//通过反射创建对象
Constructor<SingletonDemo4> declaredConstructor = SingletonDemo4.class.getDeclaredConstructor();
SingletonDemo4 singletonDemo3 = declaredConstructor.newInstance();
SingletonDemo4 singletonDemo = declaredConstructor.newInstance();
System.out.println(singletonDemo.hashCode());
System.out.println(singletonDemo3.hashCode());
}
}
结果:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at demo.singleton.SingletonDemo4.main(SingletonDemo4.java:43)
Caused by: java.lang.RuntimeException: 不要破坏单例
at demo.singleton.SingletonDemo4.<init>(SingletonDemo4.java:17)
... 5 more
思考:这样就可以保证安全吗?假如对方知道了flag这个字段呢?
示例:
package demo.singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class SingletonDemo4 {
private static boolean flag = false;
private SingletonDemo4(){
synchronized (SingletonDemo4.class){
if (!flag){
flag = true;
}else {
throw new RuntimeException("不要破坏单例");
}
}
}
private static volatile SingletonDemo4 singletonDemo4;
public static SingletonDemo4 getInstance(){
if (singletonDemo4 == null){
// 细粒度加锁
synchronized (SingletonDemo4.class){
if (singletonDemo4 == null){
singletonDemo4 = new SingletonDemo4();
}
}
}
return singletonDemo4;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//通过反射创建对象
Constructor<SingletonDemo4> declaredConstructor = SingletonDemo4.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);//1、关闭安全检查就可以达到提升反射速度2、访问私有private变量
SingletonDemo4 singletonDemo4 = declaredConstructor.newInstance();
Field flag = SingletonDemo4.class.getDeclaredField("flag");
flag.setAccessible(true);
flag.set(singletonDemo4,false);
SingletonDemo4 singletonDemo = declaredConstructor.newInstance();
System.out.println(singletonDemo.hashCode());
System.out.println(singletonDemo4.hashCode());
}
}
结果:
2133927002
1836019240
我们又把单例破坏了,怎么办呢?枚举单例
枚举单例
package demo.singleton;
public class SingletonDemo5 {
private SingletonDemo5(){
}
//定义一个枚举类
enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private SingletonDemo5 singletonDemo5;
//私有化枚举的构造函数
SingletonEnum(){
singletonDemo5=new SingletonDemo5();
}
public SingletonDemo5 getInstnce(){
return singletonDemo5;
}
}
public static SingletonDemo5 getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}