title: 单例模式
author: rack-leen
avatar: /images/favicon.png
authorDesc: 脱发典范
comments: true
copyright: true
date: 2019-5-19 00:00:00
tags:
Ensure a class has only one instance , and provide a global point of access to it.
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
/**
* 普通单例模式(饿汉式)
* 一开始就初始化,线程安全,比较常用,但容易产生垃圾
*
* 为什么线程安全?
* 类加载的方式是按需加载,且只加载一次。当单例类加载完成后,单例实例就被创建供系统使用,线程每次只能获取这一个实例。
*/
public class SingleTonCommon {
private static final SingleTonCommon singleTon = new SingleTonCommon();
/* 不让外界调用创建对象,只能让singleTon成员变量创建对象 */
private SingleTonCommon(){
}
public static SingleTonCommon getInstance(){
return singleTon ;
}
/* 单例类里面的类方法不能被外界访问 */
public static void test(){
System.out.println("普通单例模式(饿汉式)");
}
/* 可以被外界访问的方法 */
public void getTest(){
test();
}
}
/**
* 普通单例模式(懒汉式)
* 线程不安全,延迟初始化。
*
* 为什么线程不安全?
* 当两个线程同时创建单例对象,线程1到达singleTon == null , 线程1判断完后,还没有创建对象,线程2就抢占cpu资源,开始运行。
* 这时线程2也到达singleTon == null,并且判断为空,创建一个对象。之后释放cpu资源,线程1再次抢占cpu资源。
* 这时线程1还是原来的状态singleTon == null,判断为空,线程1再次创建一个对象,这就产生两个对象。
*/
public class SingleTonCommon2 {
private static SingleTonCommon2 singleTon ;
private SingleTonCommon2(){
}
public static SingleTonCommon2 getInstance(){
if (singleTon == null){
singleTon = new SingleTonCommon2();
}
return singleTon ;
}
/* 单例类里面的类方法不能被外界访问 */
public static void test(){
System.out.println("普通单例模式(懒汉式)");
}
/* 可以被外界访问的方法 */
public void getTest(){
test();
}
}
/**
* synchronized单例模式单例模式
*
* 线程安全,延迟初始化
*
* 为什么线程安全?
* 双重锁单例模式就是懒汉式的升级版,懒汉式在singleTon == null判断时可能会有其他线程能创建多的对象。
* 但是这里使用synchronized同步锁,使得每次判断只能进一个,就只能创建一个对象。
*/
public class SingleSynchronized {
private static SingleSynchronized singleTon ;
private SingleSynchronized(){
}
public static SingleSynchronized getInstance(){
// 第一次判断singleTon == null是在synchronized代码块之外判断,为了避免已经创建了对象还要进入同步代码块的情况,提高效率。
if (singleTon == null){
// 与懒汉式情况不同,当线程1到singleTon == null,并进入判断,但是还没创建线程就被线程2抢占cpu资源,然后线程2先一步创建对象
// 这时虽然线程1已经进入判断,但是因为加入了synchronized线程同步锁,synchronized保证两个线程都是为了创建同一个对象,线程2已经创建线程1就不需要再创建了。
synchronized (SingleSynchronized.class){
if (singleTon == null){
singleTon = new SingleSynchronized();
}
}
}
return singleTon ;
}
/* 单例类里面的类方法不能被外界访问 */
public static void test(){
System.out.println("synchronized单例模式");
}
/* 可以被外界访问的方法 */
public void getTest(){
test();
}
}
/** volatile可以保证可见性,同时保证JVM指令不会进行重排序。
* 创建对象是需要有几个步骤,通过jvm指令来创建的
* 创建对象步骤:获取1.singleTon对象需要的内存地址-->2.初始化singleTon对象-->3.将引用变量singleTon指向获取的内存地址
* volatile禁止jvm对指令进行重排序,因此指令顺序总是1-->2-->3
*/
public class SingleTonVolatitle {
private volatile static SingleTonVolatitle singleTon ;
private SingleTonVolatitle(){
}
/**
* 虽然volatile不能保证原子性,但是singleTon赋值堆中的引用是原子操作
* 如果被两个线程执行, new SingleTonVolatitle()会执行两次,但是赋值操作只进行一次。
* @return
*/
public SingleTonVolatitle getInstance(){
if (singleTon == null) {
singleTon = new SingleTonVolatitle();
}
return singleTon ;
}
/* 单例类里面的类方法不能被外界访问 */
public static void test(){
System.out.println("volatile单例模式");
}
/* 可以被外界访问的方法 */
public void getTest(){
test();
}
}
/**
* 静态内部类单例模式
*
* 只有第一次调用getInstance方法时,虚拟机才加载静态内部类并初始化,只有一个线程获得对象的初始化锁,其他线程无法进行初始化。
*/
public class SingleTonStaticInner {
private SingleTonStaticInner(){
}
public SingleTonStaticInner getInstance(){
return Inner.singleTon ;
}
/**
* 静态内部类只有在被加载的时候才会被初始化,并且其生命周期直到系统结束才会结束。
* 这样就保证有且只有一个对象,不管有多少为线程调用getInstance,都是取的同一个对象
* 因此,静态内部类能保证单例唯一性,延迟单例实例化以及保证线程安全
*
* 但是静态内部类无法传递参数
*/
private static class Inner{
private static final SingleTonStaticInner singleTon = new SingleTonStaticInner();
}
}
/**
* 枚举单例模式
* 枚举和普通类一样,都拥有字段和方法
* 枚举的构造方法只能被声明为私有构造方法
* 枚举是线程安全的,并且在任何情况下,它都是一个单例(我们通过枚举类.INSTANCE就能获得枚举对象)
*/
public enum SingleTonEnum {
INSTANCE;
public static SingleTonEnum Instance(){
return SingleTonEnum.INSTANCE ;
}
}
/**
* 静态代码块单例模式
* 静态代码块是在单例类在虚拟机中一加载就被调用,并且只调用一次
* 因此静态代码块单例也是线程安全的
*/
public class SingleTonStatic {
private static SingleTonStatic singleTon ;
private SingleTonStatic(){
}
static {
singleTon = new SingleTonStatic();
}
}
能产生固定数量对象的模式叫有上限的多例模式。
import java.util.ArrayList;
/**
* 单例的扩展
* 有上限的多例
*/
public class SingleTonExtension {
/* 定义需要产生的对象数量 */
private static int maxNumSingleTon = 2 ;
/* 将所有产生的对象放入这个容器,ArrayList是线程不安全的,可以使用线程安全的容器 */
private static ArrayList<SingleTonExtension> singleTons = new ArrayList<>();
private SingleTonExtension(){
}
/* 使用静态代码块实现单例 */
static {
for (int i=0 ; i<maxNumSingleTon ; i++) {
singleTons.add(new SingleTonExtension());
}
}
}