• Java线程和多线程(五)——单例类中的线程安全


    单例模式是最广泛使用的创建模式之一。在现实世界之中,诸如Databae的连接或者是企业信息系统(EIS)等,通常其创建都是受到限制的,应该尽量复用已存在对象而不是频繁创建销毁。为了达到这个目的,开发者通常会通过实现单例模式来创建一个wrapper类,来封装资源,限制其运行时所创建对象的个数。

    单例中的线程安全

    总的来说,开发者一般会按照如下的方式来创建单例的类:

    1. 使用私有构造函数来避免其它外部引用通过new的方式来创建新的对象引用。
    2. 声明一个该类的私有静态变量为实例。
    3. 提供一个公有的静态方法来返回单例的实例。如果实例还没有初始化的话,就将其初始化后再返回实例。

    通过上面的步骤,我写了一个如下的单例类:

    package com.sapphire.designpatterns;
    
    public class ASingleton {
    
        private static ASingleton instance = null;
    
        private ASingleton() {
        }
    
        public static ASingleton getInstance() {
            if (instance == null) {
                instance = new ASingleton();
            }
            return instance;
        }
    }

    在上面的代码中,getInstance()方法不是线程安全的。多线程可以在同一时间访问这个方法,而在最开始的少数线程中,实例没有初始化的时候,多线程可以进入到if代码块来创建多个实例,就破坏了单例模式。

    通常来说,有三种方式来让我们保证单例模式的线程安全。

    在class加载的时候就创建实例变量

    这种实现方式有如下优点:

    • 不需要同步即可实现线程安全
    • 容易实现

    但是也有一些缺点:

    • 过早的创建了资源,但是应用可能并不会使用这个资源,造成了资源浪费
    • 调用方式无法传入任何参数的,所以我们无法复用这个类。举例来说,如果我们希望有一个单例类能够处理所有的数据库连接信息,并希望能够传入一些数据库信息的话,我们就无法复用这个单例类了。

    同步getInstance()方法

    这种方法有如下优点:

    • 保证了线程安全
    • 调用方可以传入任何参数
    • 可以保证延迟初始化

    当然这种方法也有一些缺点:

    • 因为使用了同步方法,会锁定资源,令所有客户端的请求会优先请求锁,从而降低了性能
    • 包含了很多非必要的同步,因为一旦实例创建完成,同步就只是浪费了性能而没有任何作用了

    在if代码块中使用同步代码块

    优点:

    • 保证了线程的安全
    • 调用方可以传递参数
    • 保证了延迟初始化
    • 最小化了同步负载,仅仅在最开始的几个线程请求的时候进行同步操作

    淡然也有一定的缺点

    • 需要额外的if条件判断

    从三种方法来判断,第三种方式应该是最佳的方式来实现同步了,代码大体如下:

    package com.sapphire.designpatterns;
    
    public class ASingleton{
    
        private static ASingleton instance= null;
        private static Object mutex= new Object();
        private ASingleton(){
        }
    
        public static ASingleton getInstance(){
            if(instance==null){
                synchronized (mutex){
                    if(instance==null) instance= new ASingleton();
                }
            }
            return instance;
        }
    }

    注意,String对象非常不适合来作为同步的锁,因为String可能是复用的对象,锁定String很容易产生死锁切很难发现,所以这里使用的是Object对象来作为同步锁。

  • 相关阅读:
    ROC-RK3328-CC开源主板运行LibreELEC系统
    ssl客户端与服务端通信的demo
    ssl客户端与服务端通信的demo
    运用shell脚本 执行sftp,ftp命令
    运用shell脚本 执行sftp,ftp命令
    运用shell脚本 执行sftp,ftp命令
    [数据库基础]——快速浏览日期时间转换
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461531.html
Copyright © 2020-2023  润新知