单例模式的几种方式整理

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要重复实例化该类的对象。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优点是在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。避免对资源的多重占用(比如写文件操作)。

单例模式的实现有多种方式例如饿汉模式、懒汉模式、双重检查模式、静态内部类方式以及枚举类。其中饿汉模式、懒汉模式又有多种变形,这里只介绍其基本的方式。

1.饿汉模式

public class Singleton {
    // 饿汉模式
    public static Singleton getInstance() {
        return instance;
    }
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

}

特点:在类加载的时候就完成了实例化,避免了线程同步问题,因此线程安全。由于是在类加载时就完成了实例化,没有达到懒加载的效果。如果一直没有使用过这个实例,就造成了内存的浪费!内存资源不是特别匮乏的情况下都可以使用这种模式。简单、实用。

2.懒汉模式

public class Singleton2 {
    // 懒汉模式
    private static Singleton2 instance = null;
    public static Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
    private Singleton2() {
    }
}

特点:实现了对象的延迟加载,在一定程度节省了资源。在单线程情况下可以使用,多线程情况下不安全。原因在于 instance = new Singleton2() 这个行代码的执行可能会发生重排序,具体原因在下面双检查模式中解释。

3.双重检查

public class Singleton3 {
    // 双重检查
    private static Singleton3 instance = null;
    public static Singleton3 getInstance() {
        if (instance == null) {                   // 1
            synchronized (Singleton3.class) {  
                if (instance == null) {          // 2
                    instance = new Singleton3(); // 3
                }
            }
        }
        return instance;
    }
    private Singleton3() {
    }
}

分析:synchronized 会导致性能开销,因此尽量在必要的位置使用,将同步块的位置范围限制的尽可能小。双检查模式在代码 1 处第一次检查instance不为null,那么就不需要执行下面的加锁和初始化操作,降低了synchronized 带来的性能开销。
但上面的代码是经典的错误方式,原因在代码 3 处, instance = new Singleton3(); 这行代码大致可以分成三个步骤:

  • 1.分配对象所需要的内存
  • 2.初始化对象
  • 3.设置instance 指向刚才分配的内存地址

单线程情况下我们可以认为jvm是按照这样的顺序执行的,就算编译器优化代码被重排序,把步骤2和步骤3的顺序调换,也不影响最后结果,这种重排序是被允许的。

在多线程情况下,如果线程A执行到instance = new Singleton3()并且步骤2和步骤3发生了重排,instance 指向了刚才分配的内存地址,但还没完成初始化。这时线程B执行到代码1处,判断instance不为null,访问到了一个未被初始化的对象,得到不可预知的结果。

解决这个问题有两个思路:

1.禁止代码重排,使用volatile关键字修饰变量,禁止步骤2和步骤3的重排序。代码如下:

 private static volatile Singleton3 instance = null;

了解更多关于volatile关键字可以参考:深入理解volatile关键字

2.允许步骤2和步骤3重排序,但不允许其他线程观察到这个重排序,即下面介绍的静态内部类方式。

4.静态内部类方式

public class Singleton4 {
    private static class InstanceHolder {
        private static Singleton4 instance = new Singleton4();
    }
    private Singleton4() {
    }
    public static Singleton4 getInstance() {
        return InstanceHolder.instance;
    }
}

分析:
Singleton4 getInstance() 被调用时会触发InstanceHolder 类的初始化,在初始化阶段执行类构造器clinit方法,clinit是class类构造器对静态变量,静态代码块进行初始化。

Java虚拟机必须保证一个类的clinit方法在多线程环境中被正确地加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的clinit方法,其他线程都需要阻塞等待,直到活动线程执行完毕clinit方法,其他线程虽然会被阻塞,但如果执行clinit方法的那条线程退出clinit方法后,其他线程唤醒后则不会再次进入clinit方法。同一个类加载器下,一个类型只会被初始化一次。

在多线程情况下,A线程在调用getInstance()方法就算发生了:

  • 2.初始化对象
  • 3.设置instance 指向刚才分配的内存地址

的代码重排序也不影响线程安全,因为B线程看不到这个变化,可以这样理解:对线程B来说,InstanceHolder静态内部类实例化Singleton4对象的过程是一个原子操作。

相对于volatile双检查方式,静态内部类方式的代码更简洁。

5. 枚举类方式

代码:

public enum Singleton5 {
    INSTANCE;
    public void methodA() {
        System.out.println("枚举类方式!");
    };

分析:
枚举类本身很符合单例模式的特点,它的构造方法是私有的。
枚举类由JVM在加载的时候,实例化枚举对象,在枚举类中定义了多少个就会实例化多少个,JVM为了保证每一个枚举类元素的唯一实例,是不会允许外部进行new的,所以会把构造函数设计成private,防止用户生成实例,破坏唯一性。
代码中INSTANCE就是一个枚举类的实例。

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,认为是实现单例模式的最佳方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,防止多次实例化。

### 回答1: 电脑桌面显示配置是指对电脑桌面上的图标、背景、分辨率等进行调整和设置。在Windows操作系统中,可以通过右键点击桌面空白处选择“个性化”来打开桌面显示配置界面。在这里,我们可以更改桌面背景图片、设置图标大小和排列方式,还可以调整电脑屏幕的分辨率以适应不同大小的显示器。 另外,如果想要让电脑桌面更加个性化,我们还可以下载和安装各种小部件和皮肤软件,通过它们来自定义桌面图标和壁纸,实现更多的视觉效果和功能。 而IP地址是指互联网协议地址,是一种用于在网络上唯一识别一个设备的地址。在Windows操作系统中,可以通过命令行工具或者控制面板来查看电脑IP地址。比如,我们可以按下Win键 + R键打开运行窗口,然后输入“cmd”(不带引号)并按下回车键,就会打开命令行窗口。在命令行中,可以输入“ipconfig”命令来查看电脑IP地址、子网掩码、网关等网络配置信息。 总之,电脑桌面显示配置IP地址都是在日常使用电脑时会涉及到的一些设置和信息。这些设置可以让我们更好地个性化电脑桌面,并且了解电脑所在网络的一些基本信息。 ### 回答2: 电脑桌面显示配置是指对电脑的显示器进行设置,包括屏幕分辨率、壁纸、桌面图标、任务栏等。我们可以在“控制面板”中找到“显示”选项,然后对显示器进行个性化的设置。 首先,屏幕分辨率是指屏幕上横向和纵向的像素数量,决定了屏幕上能显示多少信息。一般而言,我们可以根据自己的需要来选择适合自己的分辨率。较高的分辨率可以提供更清晰的图像,但可能使文本和图标变得更小,较低的分辨率则相反。 其次,壁纸是指桌面背景的图像或颜色。我们可以从系统提供的壁纸中选择,也可以使用自己的图片作为壁纸。通过壁纸,我们可以个性化自己的电脑桌面,体现自己的喜好和风格。 还有,桌面图标是指显示在桌面上的各种快捷方式和文件夹图标。我们可以通过拖拽或右键菜单进行图标的增加、删除和排序,以方便我们快速访问所需的程序和文件。 最后,任务栏是显示在屏幕底部的水平栏,用于快速启动程序和切换窗口。我们可以根据自己的使用习惯自定义任务栏,比如将常用的程序固定到任务栏上,调整任务栏的位置和尺寸等。 至于IP地址,它是计算机在网络中的唯一标识。我们可以通过在命令提示符或网络设置中输入“ipconfig”命令来获取自己电脑IP地址IP地址分为IPv4和IPv6两种,一般IPv4是由四个0到255之间的数字组成,用点来分隔,例如192.168.0.1。而IPv6则更为复杂,包含八组四位十六进制数字,用冒号分隔。 总之,电脑桌面显示配置IP地址设置是为了提高用户使用电脑的便利性和个性化需求,根据自己的喜好和实际需求进行设置,以提升工作和娱乐效率。 ### 回答3: 电脑桌面显示配置指的是电脑显示器的设置和布局。我们可以通过右键点击桌面的空白位置,选择“显示设置”来进行配置。在显示设置中,可以调整分辨率、缩放比例、屏幕旋转、多个显示器的顺序和位置等。 首先,我们可以通过显示设置调整分辨率。分辨率决定了显示器上像素的数量,较高的分辨率意味着更清晰的图像。调整分辨率可以根据个人喜好或应用程序需求。 其次,我们可以调整缩放比例。缩放比例决定了在显示器上的元素的大小。较大的缩放比例使得文字和图标更大,方便阅读和操作。 另外,显示设置还可以实现屏幕的旋转。通过选择适应自己的工作或娱乐需求,可以将屏幕旋转为横向或纵向模式。 对于使用多个显示器的用户,还可以调整显示器的顺序和位置。这样可以模拟多屏幕工作环境,提高工作效率。 IP(Internet Protocol)是指互联网协议,用于在网络中标识和定位计算机和其他设备。每个连接到互联网上的设备都会被分配一个IP地址。 我们可以通过以下方法查看电脑IP地址。首先,打开命令提示符(按下Win + R,然后输入“cmd”并按Enter键),然后输入“ipconfig”命令并按Enter键。在命令的输出中,可以找到标有“IPv4地址”或“IP地址”的一行,后面的数字就是电脑IP地址。 另外,也可以在控制面板中找到网络和互联网设置,然后点击网络连接,查看本地连接的详细信息,其中包括IP地址。 通过以上方法,我们可以轻松查看电脑桌面的显示配置IP地址。这些信息对于个性化设置和网络连接都非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值