Java多线程基础-8:单例模式及其线程安全问题(一)

简介: 本文介绍了设计模式中的单例模式,它是软件开发中的经典模式,确保某个类在程序运行期间只有一个实例。

例模式是经典的设计模式之一。什么是设计模式?代码的设计模式类似于棋谱,棋谱就是一些下棋的固定套路,是前人总结出来的一些固定的打法。依照棋谱来下棋,不说能下得非常好,但至少是有迹可循,不会下得很糟糕。代码的设计模式也是一样。


设计模式,就是软件开发中的棋谱。一些编程界的大佬,针对一些常见情景总结出了一些代码的“编写套路”。按照这样的套路来写代码,不说能写得非常好,但也至少不会写得太糟糕。以前有一个大佬写了一本书,名叫《讨论二十三种设计模式》,这本书广为流传,这里的设计模式也就是我们上面说到的。


事实上设计模式远不止“二十三种”。以下两种设计模式经常遇到:


单例模式

工厂模式

本文主要介绍单例模式。



一、什么是单例模式?


单例指的就是单个实例(instance),也就是单个对象(对象就是类的实例)。单例模式指的是某个类在进程中只有唯一一个实例(在一个程序中,只能创建一个实例(一个对象),不能创建多个对象)。


按理来说,在写代码的时候多 new 几次,就能创建多个对象了。但在语法上,是有办法禁止这样多 new 几次的操作的。


也就是说,Java中的单例模式,实际上是借助 Java 语法,保证某个类只能够创建出一个实例,而不能被new多次。


为什么会有这样的用途?其实原因是很简单的:在有些场景下,本身它就要求某个概念是单例的。比如每个人只能同时拥有一个配偶。



二、如何实现单例模式?


Java实现单例模式的方式有很多种,这里我们主要介绍两种写法:


饿汉模式(急迫)

懒汉模式(从容)

如何理解 饿汉模式 和 懒汉模式 呢?饿汉模式就好比每次吃完饭之后,立刻就把碗给洗了(主打的就是一个急迫);懒汉模式则是每次吃完饭了,先把碗放到一边先不洗,等到吃下一顿了再洗。通常认为,懒汉模式更好,效率更高(非必要不洗碗)。


比如,中午吃饭用了4个碗,那么饿汉模式就得一次性把4个碗都洗了;而晚上吃饭要用2个碗,懒汉模式就只需要洗4个碗当中用不到的2个碗就行了。洗2个碗明显要比洗4个碗效率更高(不考虑没洗的碗会变臭~~只考虑效率)。


在计算机中的例子:打开一个硬盘上的文件,读取文件内容并显示出来。


饿汉:把文件所有内容都读到内存中,并显示。

懒汉:只把文件读一小部分,把当前屏幕填充上。如果用户翻页了,再读其它文件内容;如果不翻页,就省下了。

在这样的情况下,懒汉模式也是完胜饿汉模式的。


假设要读取的文件非常大,有 10G,按照饿汉模式的方式,文件打开可能都要卡半天,更何况还有内存是否足够的问题。


但懒汉模式下就可以很快速地打开,因为它只读取 1 页的内容,1 页也就几百字,可能也就读取 2k 就够了;如果用户还要读其它页的内容,懒汉再从内存里读取相应的内容,用户浏览不到的页面,也就不将它的内容加载到内存中了。


(虽然懒汉模式会增加硬盘的读取次数,但和饿汉模式的情况相比,是不值一提的。)


下面我们来看看如何用代码实现这两种单例模式。


1、代码实现:饿汉模式


a.饿汉模式的构造思路

我们先初步地创建出 Singleton 类,并在里面把对象创建出来:

// 把一个类设置成单例的
class Singleton {
    // 唯一实例的本体
    private static Singleton instance = new Singleton();    // 把对象创建出来
 
    // 获取到实例的方法
    public static Singleton getInstance() {
        return instance;
    }
}


注意:这里的 instance 属性要用 static 修饰,static变量保存了单例对象的唯一实例。



同时,将 instance 属性用private封装,并提供一个get方法。这样,我们就可以从外部获取instance了:



public class Test {
    public static void main(String[] args) {
        // 此时 s1 和 s2 是同一个对象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
 
    }
}


显然,此处的 s1 和 s2 获取到的实际是同一个对象。


但是,上述的代码并没有限定再次 new 对象的操作:



此处的 s3 也显然与 s1 和 s2 不是同一个对象。因此,此处必须把 new 操作给禁止掉。采用的方式是 构造方法私有化。


将构造方法用 private 修饰,可以发现,此时我们上面的 new 操作就报错了,无法通过编译。


有些同学可能会想到,用反射仍然可以获取到私有方法。一方面,反射本身就是一种非常规的手段,它本身就是不安全的;另一方面,单例模式有一种实现方式,借助枚举,也可以保证反射下的安全,这个在此不过多介绍。


b.总结:饿汉模式代码


class Singleton {
    private static Singleton instance = new Singleton();
 
    public static Singleton getInstance() {
        return instance;
    }
 
    private Singleton(){ }
}
public class Test {
    public static void main(String[] args) {
        //此时s1和s2是同一个对象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
    }
}


将一个类设置成单例的


通过Java语法来限制类实例的多次创建,从而实现单例模式:


static是用于类级别的数据共享,保存了单例对象的唯一实例,可以在单例模式中用来单例实例的唯一性。

单例模式中将构造方法私有化,可以避免外部直接创建新的实例。

但饿汉模式的有一个问题,那就是实例的创建时机过早了。只要类一加载,就会创建出这个实例,可要是后面并没有用到这个实例呢?


更好的实现方式是懒汉模式。


2、代码实现:懒汉模式


懒汉模式的核心思想:非必要,不创建。懒汉模式和饿汉模式的代码实现类似,最大的区别是饿汉模式在不使用instance对象时,不把它new出来。


以下代码就是懒汉模式的实现:


class SingletonLazy {
    //先令instance引用为null
    private static SingletonLazy instance = null;
 
    //获取instance实例
    public static SingletonLazy getInstance() {
        if(instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
 
    //构造方法私有化
    private SingletonLazy() { }
}





Java多线程基础-8:单例模式及其线程安全问题(二)+

https://developer.aliyun.com/article/1520525?spm=a2c6h.13148508.setting.14.75194f0eujcMau

相关文章
|
1天前
|
缓存 监控 安全
深入理解Java中的线程池和并发编程
深入理解Java中的线程池和并发编程
|
1天前
|
设计模式 安全 Java
如何在Java中实现线程安全的单例模式
如何在Java中实现线程安全的单例模式
|
1天前
|
缓存 安全 Java
如何使用Java实现高效的多线程编程
如何使用Java实现高效的多线程编程
|
1天前
|
Java 机器人 程序员
Java中的线程通信:wait、notify与Condition详解
Java中的线程通信:wait、notify与Condition详解
|
1天前
|
安全 Java 机器人
Java中的多线程编程实用指南
Java中的多线程编程实用指南
|
1天前
|
存储 安全 Java
Java中的线程安全与同步技术
Java中的线程安全与同步技术
|
29天前
|
设计模式 监控 Java
Java多线程基础-11:工厂模式及代码案例之线程池(一)
本文介绍了Java并发框架中的线程池工具,特别是`java.util.concurrent`包中的`Executors`和`ThreadPoolExecutor`类。线程池通过预先创建并管理一组线程,可以提高多线程任务的效率和响应速度,减少线程创建和销毁的开销。
40 2
|
1月前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
28 1
|
1月前
|
Java 调度
Java多线程:什么是线程池(ThreadPool)?
Java多线程:什么是线程池(ThreadPool)?
59 0
|
29天前
|
设计模式 安全 Java
Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)
Java 多线程系列Ⅳ(单例模式+阻塞式队列+定时器+线程池)