java设计模式详细讲解-单例模式

作者:超级无敌大飞   阅读 (2279)  |  收藏 (0)  |  点赞 (0)

摘要

本文将讲述23种设计模式中的单例模式的原理和应用场景,同时结合案例来讲述如何使用。


原文链接:java设计模式详细讲解-单例模式

如果想要更加详细的 Java中的23种设计模式视频资源,请点击链接:Java中的23种设计模式视频资源下载

1、经典单例模式原理

在项目实践中,我们经常需要使用类似于线程池、缓存、数据库连接池等,这些对象不管我们使用多少次,它只会是存在一个对象,并且多个实例调用这个对象时,也不会让数据混乱,这就是单例模式中的意义。

通常一般使用静态变量或者全局变量来简单的达到上面的目的。

总结一下,单例模式就是确保一个类最多只能有一个实例,并且提供一个全局访问点,并且不会造成数据混乱的一种设计思想。

单例模式的类图如下:

单例设计模式的类图

2、经典单例模式代码示例

单例模式示例很简单,代码如下:

package com.beBianMin.shareLogger.singleton;

import java.security.Signature;

/**
 * 单例模式获取单例实例的类
 * @author 郭鹏飞
 *
 */

public class SingletonClass {
	/**
	 * 一个私有的全局变量类
	 */
	private static SingletonClass singletonClass;
	
	/**
	 *将空构造函数私有化,防止其他类直接通过构造函数来实例化对象实例 
	 */
	private SingletonClass() {
		
	}
	
	/**
	 * 提供一个公共的获取实例的静态方法
	 */
	public static SingletonClass getInstance(){
		if(singletonClass == null) {
			singletonClass = new SingletonClass();
		}
		return singletonClass;
	}
	
	public void doAnyThing() {
		if(singletonClass !=null) {
			System.out.println("本实例对象为:"+singletonClass);
		}else {
			System.out.println("还没有实例化对象哦");
		}
	}
	
}

测试用例:

package com.beBianMin.shareLogger.singleton;

public class TestClass {
	public static void main(String[] args) {
		SingletonClass singletonClass = SingletonClass.getInstance();
		singletonClass.doAnyThing();
		
		SingletonClass singletonClass1 = SingletonClass.getInstance();
		singletonClass1.doAnyThing();
		
		System.out.println(singletonClass.equals(singletonClass1));
	}
}

打印结果如下:

本实例对象为:com.beBianMin.shareLogger.singleton.SingletonClass@15db9742
本实例对象为:com.beBianMin.shareLogger.singleton.SingletonClass@15db9742
true

通过分析打印结果来看,创建的多个对象其实引用的堆栈完全一致,说明多个对象其实i就是一个实例。

3、单例模式总结及优化

3.1 总结

综上所述,单例模式有如下特点:

1、单例模式类的构造方法为私有的;

2、类内部定义了一个静态的某个对象的全局变量;

3、提供一个静态的获取本实例的public 方法

4、提供对外的业务处理方法(业务处理,非必要)

3.2 优化

上面示例中,基本运行没问题,但是在多线程下会出现问题,我们都知道,多线程是分片执行的,例如这个单例对象还未被实例化,而两个线程同时访问上面的SingletonClass类,在通过下面的方法获取实例对象时

public static SingletonClass getInstance(){
    f(singletonClass == null) {
        singletonClass = new SingletonClass();
    }
    return singletonClass;
}

假如线程A访问完下面这行代码时候,

if(singletonClass == null) {

发现该单例对象还没有被实例化,于是本应该new 一个实例,但在这时,线程被切换为B线程,B线程也通过上面这行代码发现实例也未被创建,于是B线程就new 了个实例并返回了;此时,线程A又被切回来了,于是A也new了个实例并反悔了。此时就出现了多线程的问题,本应该是单例的,却产生了多个实例,违背了我么设计的初衷。

明白了问题所在,我们就可以通过不同的方式来解决这个问题。

1、获取实例的方法上加一个同步锁
public static synchronized SingletonClass getInstance(){
    f(singletonClass == null) {
        singletonClass = new SingletonClass();
    }
    return singletonClass;
}

通过这种加synchronized关键字,在多线程下不管有多少个线程通过该方法获取实例,在调用该方法前,必须等待上一个调用该方法的请求完成后才能调用,杜绝了多个请求获取不同实例的问题;但是由于必须是一个请求调用完才能让另一个请求调用,这就造成了性能和效率大打折扣的问题,针对这个问题,我们继续用另外一种方式优化。

2、在设置的静态全局变量中直接初始化该实例(急切创建实例)。

	/**
	 * 一个私有的全局变量类
	 */
	private static SingletonClass singletonClass = new SingletonClass();

而在获取实例的方法中直接返回该创建后的实例

	/**
	 * 提供一个公共的获取实例的静态方法
	 */
	public static synchronized SingletonClass getInstance(){
//		if(singletonClass == null) {
//			singletonClass = new SingletonClass();
//		}
		return singletonClass;
	}

这种方式的好处在于解决了上面多线程中访问效率的问题,不管你是否调用,我都在编译这个累的时候直接就初始化一个实例,完全杜绝了多线程中被创建多个实例或者效率的问题;但是这个方法仍旧有缺点,那就是如果这个类一次都没有呗用过,那也会被创建这个实例,这就造成了资源浪费,因此我们提出第三种方案

3、双重检查加锁法

首先在静态的全局属性中加上volatile关键词,这个目的就是为了解决线程安全。

	/**
	 * 一个私有的全局变量类
	 */
	private volatile static SingletonClass singletonClass = null;

然后在获取实例的方法中加上同步锁

	/**
	 * 提供一个公共的获取实例的静态方法
	 */
	public static synchronized SingletonClass getInstance(){
		if(singletonClass == null) {
			synchronized (SingletonClass.class) {
				if(singletonClass == null) {
					singletonClass = new SingletonClass();
				}
			}
		}
		return singletonClass;
	}

如果在多线程下同时有多个线程访问到上面方法中的第一个if中,在即将访问如下方法时,都需要先等待上一个请求执行完

synchronized (SingletonClass.class) {

同时在进入这个代码时,还需要再次判断对象实例是否为空,由于加了同步锁,此时再次判断时如果上一个线程已经new了一个实例,那么肯定直接返回这个实例,窦泽才会创建实例。这种方法比第一种方法优化了很多。

除了经典单例模式,上面的优化方案需要根据你的项目实际需要来选择不同的优化。

分类   java 设计模式
字数   3524

博客标签    单例模式讲解   java 设计模式值单例模式  

评论