单例模式

了解一下

核心作用

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常见的五种单例模式的是实现方式

  • 饿汉式(线程安全,调用效率高,不能延时加载)
  • 懒汉式(线程安全,调用效率不高,可以延时加载)
  • DCL懒汉式(由于JVM底层内部模型原因,偶尔会出现问题,不推荐使用)
  • 饿汉式改进(静态内部类,线程安全,调用效率高,可以延时加载)
  • 枚举单例(线程安全,调用效率高,不能延时加载)

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//饿汉式单例模式
public class demo1 {
//1. 私有化构造器
private demo1(){
}
// 2. 类初始化的时候,立即加载该对象;由于用了static关键字,在类装载的时候就初始化对象,
// 不存在并发问题,因为一加载就出来了
private static demo1 instance=new demo1();
//3. 提供获取该对象的方法;由于不存在并发问题,所以没有加synchronized关键字,效率高;如果考虑到并发情况,是要加synchronized关键字的;
public static demo1 getInstance(){
return instance;
}
}
//问题:如果在这个类中,加了一些开辟空间的方法,那么不管我用不用这个类,这个块空间都被占用了,
//浪费了资源,理想情况应该是在调用getInstance方法的时候,再去开辟空间;
class demo1Test{
public static void main(String[] args) {
demo1 instance=demo1.getInstance();
demo1 instance1=demo1.getInstance();
System.out.println(instance==instance1);
}
}

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class demo2 {
//1. 私有化构造器
private demo2(){
}
//2. 类初始化的时候,不立即加载该对象
private static demo2 instance;
//3. 提供获取该对象的方法,由于可能会出现有多个线程来进来的话,需要让他们排队,否则都有问题
// 所以有synchronized同步这个关键字,效率低!
public static synchronized demo2 getInstance(){
if(instance==null)
{
instance=new demo2();
}
return instance;
}
}
class demo2Test{
public static void main(String[] args) {
demo2 instance=demo2.getInstance();
demo2 instance1=demo2.getInstance();
System.out.println(instance==instance1);
}
}

DCL懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//DCL懒汉式
public class demo3 {
private demo3(){
}
//2. 类初始化的时候,不立即加载该对象
private volatile static demo3 instance;
//volatile是后来加的
//3. 提供获取该对象的方法,由于可能会出现有多个线程来进来的话,需要让他们排队,否则都有问题
// 所以有synchronized同步这个关键字,效率低!
//不用synchronized可以用synchronized代码块,锁demo3这个类本身;双重检测
//分析:现在不需要对整个方法进行同步了,将锁的范围变得更精细了,如果有个进程进来了,发现
//这个instance对象没有被创建,有一个锁,他首先要和其他进程竞争本类的锁,获得锁之后,再次检查,
//如果还是null,说明他是第一个竞争到这个锁的,于是他这个线程就负责创建这个对象,其他的线程
//进来之后,直接调用即可
public static demo3 getInstance(){
if(instance==null)
{
synchronized (demo3.class){
if(instance==null)
{
instance=new demo3();
}
}
}
return instance;
}
}
//由于这个操作不是原子性操作,所以他会经过下面几个步骤
//1.分配内存
//2. 执行构造方法
//3. 执行地址
//可能会出现的问题:极端情况,一个线程进来了,走到instance=new demo3(),还没有出去方法,
//结果另一个线程进来了,就会直接走到 return instance;,这样instance就是一个新的对象,
//破环了单例模式,可能会发生一些意想不到的问题;在这种情况下加volatile关键字;
//volatile可以保证一个线程在对这个变量进行修改的时候,另一个线程,该变量的缓存就失效了,直接读内存中的值
class demo3Test{
public static void main(String[] args) {
demo3 instance=demo3.getInstance();
demo3 instance1=demo3.getInstance();
System.out.println(instance==instance1);
}
}

静态内部类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//静态内部类实现
public class demo4 {
private demo4(){
}
private static class InnerClass{
private static final demo4 instance=new demo4();
}
public static demo4 getInstance(){
return InnerClass.instance;
}
}
//反射机制可以破坏private
class demo4Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
demo4 instance=demo4.getInstance();
//通过反射拿到instance
Constructor<demo4> demo4Constructor=demo4.class.getDeclaredConstructor(null);
demo4Constructor.setAccessible(true);
demo4 instance1=demo4Constructor.newInstance();
System.out.println(instance==instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}

优化静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class demo5 {
private demo5(){
synchronized (demo5.class){
if(instance!=null)
{
throw new RuntimeException("不要试图用反射破坏单例模式!");
}
}
}
//2. 类初始化的时候,不立即加载该对象
private volatile static demo5 instance;
public static demo5 getInstance(){
if(instance==null)
{
synchronized (demo5.class){
if(instance==null)
{
instance=new demo5();
}
}
}
return instance;
}
}
class demo5Test{
public static void main(String[] args) throws Exception {
demo5 instance=demo5.getInstance();
//通过反射拿到instance
Constructor<demo5> demo4Constructor=demo5.class.getDeclaredConstructor(null);
demo4Constructor.setAccessible(true);
demo5 instance1=demo4Constructor.newInstance();
System.out.println(instance==instance1);
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//枚举
//反射不能够破坏枚举
public enum demo6 {
INTERFACE;
public demo6 getInstance(){
return INTERFACE;
}
}
class demo6Test{
public static void main(String[] args) {
demo6 anInterface = demo6.INTERFACE;
demo6 anInterface2 = demo6.INTERFACE;
System.out.println(anInterface==anInterface2);
}
}