<commit_before>---
layout: post
title: "详解单例"
date: 2015-07-20
categories: Java
---

* content
{:toc}

## 单例

什么是单例?

> * 类只有一个唯一实例

### Java中的单例

单例的两种方式：

> * 1.饱汉式单例
> * 2.饿汉式单例

#### 饿汉式单例

什么是饿汉式单例?

> * 饿汉,就是实例对象一开始就已创建好(这里的开始指的是类初始化阶段);

经典写法如下：

	public class HungrySingle {
		//声明一个WellFedSingle对象并初始化实例对象,设计成私有
		private static HungrySingle single = new HungrySingle();
		
		//重写私有构造方法,防止外部进行实例化(但是依旧可以通过反射进行实例化)
		private HungrySingle() {
			System.out.println("类进行实例化...");
		}
		
		//公开给外部一个方法,获取single对象
		public static HungrySingle getInstance() {
			return single;
		}

		public void testOfSingle(){
			System.out.println("这是HungrySingle类对象的一个测试方法...");
		}
	}

这样一个饿汉式单例就完成了,但是这个类存在两个隐患和一个优势：

我们先通过javap -verbose命令查看此类的字节码文件:

	Constant pool:
		... //过滤掉常量池
	{
	   static {};
		descriptor: ()V
		flags: ACC_STATIC
		Code:
			stack=2, locals=0, args_size=0
			0: new           #1                  // class cn/march/algo/string/HungrySingle
			3: dup
			4: invokespecial #10                 // Method "<init>":()V
			7: putstatic     #13                 // Field single:Lcn/march/algo/string/HungrySingle;
			10: return
			LineNumberTable:
			line 6: 0
			LocalVariableTable:
			Start  Length  Slot  Name   Signature

	}

通过代码我们可以看到static HungrySingle single = new HungrySingle();这条语句其实转换成static语句块中执行

static{}语句块实际相当于clinit初始化方法(对应类加载过程的初始化阶段);在此static块中调用init方法(即类的构造方法,实例化类对象)

对类加载阶段,static块请参考[JVM类加载](http://xiaohuishu.net/2015/06/15/%E6%8E%A2%E7%A9%B6JVM%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/)

在JVM中规定:

> * 虚拟机会保证clinit方法在多线程环境下被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个类执行clinit方法,其他线程都需要堵塞等待;

所以饿汉式单例是线程安全的;

优势:

> * 1.此类是线程安全;

缺点:

> * 2.类如果继承Serializable接口,能够被反序列化创建新的实例(如果类不重写readResolve方法做处理);
> * 3.能够通过反射攻击(即可以通过反射API访问类的资源(比如获取构造器Constructor对象进行实例化))

详情请参考[Java单例讨论](http://m.blog.csdn.net/blog/three_man/45174059)

#### 饱汉式单例(懒汉)

什么是饱汉式单例?

> * 指类实例对象是一个懒加载的过程

经典写法如下:

	public class WellFedSingle {
		//声明静态类实例对象,初始化为null
		private static WellFedSingle single = null;
		
		//重写构造方法,设为私有
		private WellFedSingle() {
			System.out.println("类进行实例化...");
		}

		public static WellFedSingle getInstance() {
			//进行判断,如果single为null(即还没有创建对象),进行实例化操作
			if(single == null){
				single = new WellFedSingle();
			}
			
			return single;
		}
	}

这样一个饱汉式单例产生了,但是这个类有3个隐患:

隐患:

> * 1.线程不安全(PS:如下图);

![single](http://xiaohuishu.net/static/post_image/single.png)

线程A执行if(single == null){ ... }的时候线程B此时也执行到if(single == null):

> * 1.而A并没有实例化,所以B正好完成实例化操作,B完成实例化操作之后,A并没有及时获取single状态也对single进行实例化操作;
> * 2.A完成实例化操作,但是B并没有及时响应获取single状态,所以B对single进行实例化操作;

线程A,B都产生了不同的single对象;

所以这就产生了线程安全问题;

解决方案:
	
	//使用JDK1.5之后的volatile保证single对象的可见性,但是仅仅光声明volatile不行,还需同步处理
	private static volatile WellFedSingle single = null;
	//为什么要双重判断,因为如果没有 //1 的检查，那么所有的getInstance()都会进入锁争夺，会影响性能，因此加入了检查。 
	public static WellFedSingle getInstance() {
		if(single == null) { //1
			synchronized(WellFedSingle.class) {
				//进行判断,如果single为null(即还没有创建对象),进行实例化操作
				if(single == null){
					single = new WellFedSingle();
				}
			}
		}
		return single;<commit_msg>update single<commit_after>---
layout: post
title: "详解单例"
date: 2015-07-20
categories: Java
---

* content
{:toc}

## 单例

什么是单例?

> * 类只有一个唯一实例

### Java中的单例

单例的两种方式：

> * 1.饱汉式单例
> * 2.饿汉式单例

#### 饿汉式单例

什么是饿汉式单例?

> * 饿汉,就是实例对象一开始就已创建好(这里的开始指的是类初始化阶段);

经典写法如下：

	public class HungrySingle {
		//声明一个WellFedSingle对象并初始化实例对象,设计成私有
		private static HungrySingle single = new HungrySingle();
		
		//重写私有构造方法,防止外部进行实例化(但是依旧可以通过反射进行实例化)
		private HungrySingle() {
			System.out.println("类进行实例化...");
		}
		
		//公开给外部一个方法,获取single对象
		public static HungrySingle getInstance() {
			return single;
		}

		public void testOfSingle(){
			System.out.println("这是HungrySingle类对象的一个测试方法...");
		}
	}

这样一个饿汉式单例就完成了,但是这个类存在两个隐患和一个优势：

我们先通过javap -verbose命令查看此类的字节码文件:

	Constant pool:
		... //过滤掉常量池
	{
	   static {};
		descriptor: ()V
		flags: ACC_STATIC
		Code:
			stack=2, locals=0, args_size=0
			0: new           #1                  // class cn/march/algo/string/HungrySingle
			3: dup
			4: invokespecial #10                 // Method "<init>":()V
			7: putstatic     #13                 // Field single:Lcn/march/algo/string/HungrySingle;
			10: return
			LineNumberTable:
			line 6: 0
			LocalVariableTable:
			Start  Length  Slot  Name   Signature

	}

通过代码我们可以看到static HungrySingle single = new HungrySingle();这条语句其实转换成static语句块中执行

static{}语句块实际相当于clinit初始化方法(对应类加载过程的初始化阶段);在此static块中调用init方法(即类的构造方法,实例化类对象)

对类加载阶段,static块请参考[JVM类加载](http://xiaohuishu.net/2015/06/15/%E6%8E%A2%E7%A9%B6JVM%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/)

在JVM中规定:

> * 虚拟机会保证clinit方法在多线程环境下被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个类执行clinit方法,其他线程都需要堵塞等待;

所以饿汉式单例是线程安全的;

优势:

> * 1.此类是线程安全;

缺点:

> * 2.类如果继承Serializable接口,能够被反序列化创建新的实例(如果类不重写readResolve方法做处理);
> * 3.能够通过反射攻击(即可以通过反射API访问类的资源(比如获取构造器Constructor对象进行实例化))

详情请参考[Java单例讨论](http://m.blog.csdn.net/blog/three_man/45174059)

#### 饱汉式单例(懒汉)

什么是饱汉式单例?

> * 指类实例对象是一个懒加载的过程

经典写法如下:

	public class WellFedSingle {
		//声明静态类实例对象,初始化为null
		private static WellFedSingle single = null;
		
		//重写构造方法,设为私有
		private WellFedSingle() {
			System.out.println("类进行实例化...");
		}

		public static WellFedSingle getInstance() {
			//进行判断,如果single为null(即还没有创建对象),进行实例化操作
			if(single == null){
				single = new WellFedSingle();
			}
			
			return single;
		}
	}

这样一个饱汉式单例产生了,但是这个类有3个隐患:

隐患:

> * 1.线程不安全(PS:如下图);

图中HungrySingle类应该改为WellFedSingle类(因为图文件找不到了,见谅!!!)

![single](http://xiaohuishu.net/static/post_image/single.png)

线程A执行if(single == null){ ... }的时候线程B此时也执行到if(single == null):

> * 1.而A并没有实例化,所以B正好完成实例化操作,B完成实例化操作之后,A并没有及时获取single状态也对single进行实例化操作;
> * 2.A完成实例化操作,但是B并没有及时响应获取single状态,所以B对single进行实例化操作;

线程A,B都产生了不同的single对象;

所以这就产生了线程安全问题;

解决方案:
	
	//使用JDK1.5之后的volatile保证single对象的可见性,但是仅仅光声明volatile不行,还需同步处理
	private static volatile WellFedSingle single = null;
	//为什么要双重判断,因为如果没有 //1 的检查，那么所有的getInstance()都会进入锁争夺，会影响性能，因此加入了检查。 
	public static WellFedSingle getInstance() {
		if(single == null) { //1
			synchronized(WellFedSingle.class) {
				//进行判断,如果single为null(即还没有创建对象),进行实例化操作
				if(single == null){
					single = new WellFedSingle();
				}
			}
		}
		return single;
{'fraction_non_alphanumeric': 0.13197805743788318, 'fraction_numerical': 0.02291061632784769, 'mean_word_length': 4.023500810372772, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '225481', 'n_tokens_mistral': 3787, 'n_tokens_neox': 3792, 'n_words': 351}