Monkey 平 Monkey 平
首页
  • JAVA知识点
  • Docker
  • Linux
友情链接
关于
GitHub (opens new window)

Evan Xu

前端界的小学生
首页
  • JAVA知识点
  • Docker
  • Linux
友情链接
关于
GitHub (opens new window)
  • 设计模式

    • JAVA设计模式-单例模式
      • JAVA设计模式-单例模式
        • 单例模式
        • 实现
        • 饿汉模式
        • 写法一
        • 介绍
        • 代码示例
        • 写法二
        • 介绍
        • 代码示例
        • 懒汉模式
        • 写法一
        • 介绍
        • 代码示例
        • 写法二
        • 介绍
        • 代码示例
        • 写法三
        • 介绍
        • 代码示例
        • 写法四
        • 介绍
        • 代码示例
    • JAVA设计模式-工厂模式
    • JAVA设计模式-建造者模式
    • JAVA设计模式-原型模式
    • JAVA设计模式-适配器模式
    • JAVA设计模式-装饰模式
    • JAVA设计模式-外观模式
    • JAVA设计模式-代理模式
    • JAVA设计模式-桥接模式
  • JAVA知识点
  • 设计模式
Monkey 平
2022-09-17
目录

JAVA设计模式-单例模式

# JAVA设计模式-单例模式

# 单例模式

类只能有一个实例,在内存中会创建并且只创建一次对象。所有其他类或者其他需要调用的地方都是用这一个对象,可以防止频繁创建对象,内存占用高。特点:只能有一个实例,并且能够自行创建这个实例的类。

# 实现

# 饿汉模式

# 写法一
# 介绍

在类的加载时就已经创建好对象,线程是安全的,但是会浪费资源。参考源码:JDK1.8 com.sun.glass.ui.monocle.KeyInput类

image-20220916180618157

# 代码示例
public class Singleton {
    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象
     */
    private static Singleton singleton = new Singleton();

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static Singleton getInstance(){
        return singleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 写法二
# 介绍

在类的加载时就已经创建好对象,此处是使用静态代码块进行创建,线程是安全的。

image-20220916221335985

# 代码示例
public class Singleton {

    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象
     */
    private static Singleton singleton = null;

    static {
        singleton = new Singleton();
    }

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static Singleton getInstance(){
        return singleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 懒汉模式

# 写法一
# 介绍

在第一次使用时进行创建对象,如果多个线程同时调用了getInstance()方法,可能会创建多个对象,线程是不安全的。

image-20220916182044256

# 代码示例
public class Singleton {

    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象,注意此处不进行new创建
     */
    private static Singleton singleton;

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static Singleton getInstance(){
        // 多个线程同时执行这边时,可能会创建多个对象
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 写法二
# 介绍

这种写法也是在第一次调用时进行创建对象,但是在该方法上增加了synchronized同步锁,这样就可以保证只会有一个线程执行。线程是安全的,但是synchronized在1.6之前性能比较差,1.6之后进行了优化,性能提升,但是此处为了保证线程安全,使用synchronized,还是多少会影响性能。

image-20220916215630181

# 代码示例
public class Singleton {

    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象,注意此处不进行new创建
     */
    private static Singleton singleton;

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static synchronized Singleton getInstance(){
        // 第一次调用时进行创建对象
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 写法三
# 介绍

这种写法采用双重锁的方式,进行判断并创建对象。线程是不安全的。

image-20220916224546554

分析线程不安全原因:首先在内存中创建对象,需要进行

①分配内存地址

②初始化对象

③设置对象指向刚刚分配的内存地址

但是在编译器、指令集并行、内存中都可能进行指令重排序。如果发生指令重排序,对于单线程来说,如果执行①③②和执行①②③的结果是一样的,但是如果是多线程,在线程A执行完①③之后,如果线程B进入到第一个if语句,则会判断singleton已经指向一个地址,不等于null,则直接返回,此时返回的对象是还未进行初始化的空对象。

# 代码示例
public class Singleton {

    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象,注意此处不进行new创建
     */
    private static Singleton singleton;

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static Singleton getInstance(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
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
# 写法四
# 介绍

在写法三的基础上,增加volatile,volatile可以禁止指令重排序并且保证共享变量不同线程之间的可见性。

image-20220916233723442

# 代码示例
public class Singleton {

    /**
     * 私有的构造方法,可以防止外部调用时new进行创建对象
     */
    private Singleton(){};
    /**
     * 创建私有的对象,注意此处不进行new创建, 使用volatile
     */
    private volatile static Singleton singleton;

    /**
     * 提供公共的对外的获取方法
     * @return
     */
    public static Singleton getInstance(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
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
编辑 (opens new window)
上次更新: 2023/02/26, 10:03:01
JAVA设计模式-工厂模式

JAVA设计模式-工厂模式→

最近更新
01
Linux文件夹权限操作
10-30
02
Linux基础知识一
10-25
03
JAVA设计模式-代理模式
10-18
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式