Java设计模式3:单例模式

本文深入解析单例模式,包括其定义、特点及应用场景,并详细介绍了饿汉式、懒汉式和双重检索式三种常见实现方式。

前言:

单例模式是一种常用的软件设计模式,在我们项目与所用到的各种框架中会经常见到。本文就详细介绍下,几种单例模式的常见写法。

一、单例模式:

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。

二、单例模式特点:


1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。

下面就介绍下单例模式的三种写法,饿汉式、懒汉式、双重检索式。其他的静态内部类的方式、静态代码块的方式、enum枚举的方式也都可以,不过异曲同工,这里只介绍前面三种比较常见的。

第一种:饿汉式

顾名思义,饿汉式,就是使用类的时候不管用的是不是类中的单例部分,都直接创建出单例类,看一下饿汉式的写法:

/**
 * <pre>
 *    @author : orange
 *    @e-mail : 495314527@qq.com
 *    @time   : 2018/9/29 11:15
 *    @desc   : 饿汉式单例
 *    @version: 1.0
 * </pre>
 */
public class HungrySingleton {

    private static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getHungrySingleton(){
        return hungrySingleton;
    }
}

这就是饿汉式单例模式的写法,也是一种比较常见的写法。这种写法会不会造成竞争,引发线程安全问题呢?答案是不会。可能有人会觉得奇怪:

代码的第3行(private static HungrySingleton hungrySingleton = new HungrySingleton();),CPU执行线程A,实例化一个HungrySingleton,没有实例化完,CPU就从线程A切换到线程B了,线程B此时也实例化这个HungrySingleton,然后HungrySingleton被实例化出来了两次,有两份内存地址,不就有线程安全问题了吗?

没关系,我们完全不需要担心这个问题,JDK已经帮我们想到了。Java虚拟机2:Java内存区域及对象,文中可以看一下对象创建这一部分,没有写得很详细,其实就是"虚拟机采用了CAS配上失败重试的方式保证更新更新操作的原子性和TLAB两种方式来解决这个问题"。

 

第二种:懒汉式

只有当单例类用到的时候才会去创建这个单例类,看一下懒汉式的写法:

/**
 * <pre>
 *    @author : orange
 *    @e-mail : 495314527@qq.com
 *    @time   : 2018/9/29 11:24
 *    @desc   : 懒汉式单例  线程不安全,很少用
 *    @version: 1.0
 * </pre>
 */
public class LazySingleton {

    private static LazySingleton singleton = null;

    private LazySingleton(){}

    public static LazySingleton getSingleton(){

        if(null == singleton){
            singleton =  new LazySingleton();
        }
        return singleton;
    }
}

懒汉式单例模式这种写法在多线程环境下会产生多个single对象,所以是线程不安全的,一般都很少用。下面的双重检索模式其实就是对这种线程不安全的改造。

 

第三种:双重检索式

/**
 * <pre>
 *    @author : orange
 *    @e-mail : 495314527@qq.com
 *    @time   : 2018/9/29 11:28
 *    @desc   : 双重检索方式,线程安全
 *    @version: 1.0
 * </pre>
 */
public class DoubleCheckLockSingleton {

    private static DoubleCheckLockSingleton singleton = null;

    private DoubleCheckLockSingleton(){}

    public static DoubleCheckLockSingleton getSingleton(){

        if(null == singleton){
            synchronized (DoubleCheckLockSingleton.class){
                if(null == singleton){
                    singleton =  new DoubleCheckLockSingleton();                }
            }
        }

        return singleton;
    }

}


总结:

单例模式的好处

作为一种重要的设计模式,单例模式的好处有:

1、控制资源的使用,通过线程同步来控制资源的并发访问

2、控制实例的产生,以达到节约资源的目的

3、控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

 

 

### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值