【Java设计模式】设计模式之单例详解

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证应用中一个单例类只有且只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并提供全局访问点。

  1. 实现单例三要素

教科书中的示例是很简单的,要想实现一个单例类,只需要三点:
1、定义私有的静态成员
2、私有化构造方法
3、提供公有的静态工厂方法

  1. 单例的种类(饿汉模式/懒汉模式)

饿汉模式:类加载的时候都会去创建实例;
懒汉模式:只在外部对象第一次请求实例的时候才去创建,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回。

  1. 两种模式的区别

懒汉模式在类加载的时候比较快,获取对象比较慢;
饿汉模式在类加载的时候比较慢,获取对象比较快;
注: 饿汉模式是线程安全的,而懒汉模式是线程不安全的

4.单例两种类型的实现

饿汉式单例

package com.wxl.singleton;

/**
 * @author wxl
 * @remark 单例模式--饿汉式实现
 * 		         作用:保证整个应用程序中某个实例有且只有一个
 * 		         类型:饿汉模式/懒汉模式
 * @version 1.0
 */
public class SingletonEager {

	/**
	 * 创建类的唯一实例
	 * 类加载时即创建
	 */
	private static SingletonEager instance = new SingletonEager();
	
	/**
	 * 将构造方法私有化,不允许外部直接创建对象
	 */
	private SingletonEager(){
		
	}
	
	/**
	 * 提供一个外部可以访问的获取实例方法
	 * @return
	 */
	public static SingletonEager getInstance(){
		return instance;
	}
}

懒汉式单例

package com.wxl.singleton;

/**
 * @author wxl
 * @remark 单例模式--懒汉式实现
 * 		         作用:保证整个应用程序中某个实例有且只有一个
 * 		         类型:饿汉模式/懒汉模式
 * @version 1.0
 */
public class SingletonLazy {

	/**
	 * 创建类的唯一实例
	 */
	private static SingletonLazy instance = null;
	
	/**
	 * 将构造方法私有化,不允许外部直接创建对象
	 */
	private SingletonLazy(){
		
	}
	
	/**
	 * 提供一个外部可以访问的获取实例方法
	 * @return
	 */
	public static SingletonLazy getInstance() {
		if (instance == null) {
			instance = new SingletonLazy ();
		}
		return instance;
	}
}

静态内部类方式(企业用的最多)

兼并了并发高效调用和延迟加载的优势

package com.dw.develop;

/**
 * 静态内部类实现单例(另一种懒加载方式)
 *
 * @author Administrator
 * @version v1.0
 * @date 2018-12-20
 * @modify
 */
public class Singleton {

    /**
     * 私有化构造器
     */
    private Singleton() {

    }

    /**
     * 静态内部类
     */
    private static class SingletonClass {
        private static final Singleton instance = new Singleton();
    }

    /**
     * 提供外部访问单例方法
     *
     * @return
     */
    public static Singleton getInstance() {
        return SingletonClass.instance;
    }
}
  1. 单例模式线程测试类

package com.wxl.singleton;

public class SingletonThreadSecurity implements Runnable {

	@Override
	public void run() {
		// 饿汉模式
		SingletonEager se = SingletonEager.getInstance();
		System.out.println("SingletonEager hashCode is :" + se.hashCode());
		// 懒汉模式
		SingletonLazy sl = SingletonLazy.getInstance();
		System.out.println("SingletonLazy hashCode is :" + sl.hashCode());
	}

}

  1. 单例模式单元测试类

package com.wxl.singleton;

import static org.junit.Assert.*;

import org.junit.Test;

public class SingletonTest {

	/**
	 * 测试饿汉模式
	 */
	@Test
	public void testSingletonEager() {
		SingletonEager se1 = SingletonEager.getInstance();
		SingletonEager se2 = SingletonEager.getInstance();
		System.out.println(se1.hashCode());
		System.out.println(se2.hashCode());
		assertEquals(se1, se2);
	}

	/**
	 * 测试懒汉模式
	 */
	@Test
	public void testSingletonLazy(){
		SingletonLazy sl1 = SingletonLazy.getInstance();
		SingletonLazy sl2 = SingletonLazy.getInstance();
		System.out.println(sl1.hashCode());
		System.out.println(sl2.hashCode());
		assertEquals(sl1, sl2);
	}
	
	/**
	 * 测试线程安全
	 */
	@Test
	public void testThreadSecurity(){
		SingletonThreadSecurity sts = new SingletonThreadSecurity();
		Thread t1 = new Thread(sts);
		t1.start();
		Thread t2 = new Thread(sts);
		t2.start();
	}
	
	/**
     * 测试静态内部类实现单例
     */
    @Test
    public void testStaticClass(){
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        Assertions.assertEquals(instance1, instance2);
    }
}

7.测试结果

饿汉模式测试
这里写图片描述
懒汉模式测试
这里写图片描述
饿汉模式与懒汉模式多线程测试
这里写图片描述
静态内部类测试
在这里插入图片描述

测试结论:经过大量的实验,多线程情况下懒汉模式实现的单例所得到的实例不是唯一的,而饿汉模式得到的实例始终是同一个;

  1. 懒汉模式线程不安全解决方案

	/**
	 * 线程同步锁
	 */
	private static Object synchrolock = new Object();
	
	/**
	 * 提供一个外部可以访问的获取实例方法
	 * @return
	 */
	public static SingletonLazy getInstance() {
		synchronized (synchrolock) {
			// 不存在时才实例化
			if (instance == null) {
				instance = new SingletonLazy();
			}
 
		}
		return instance;
	}
  1. 内存回收

单例的内存问题也是值得关注的,一量单例创建以后,静态变量instance就会持有一份内存引用,而且由于其static性质,这份内存将在程序运行期间持续占用,无法通过GC进行回收。所以对内存敏感的程序要减少对单例的使用,或者妥善处理内存回收问题。
10. 小结

总之,虽然单例看上去很美,但还是有这么多需要认真思考、妥善决策的技术要点,用好单例并不像我们在教科书中看到的那样简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值