一、为什用单例模式
单例模式的运用场景可以辅助我们理解。但是我觉得,仅仅单例模式的定义就可以告诉我们什么时候用,甚至我们可以想到更多适合运用的场景。
定义:在系统中确保一个类有且只有一个实例,并为它提供一个全局访问点。其实所有的需要的场景都是由于这个地方需要或者必须只能有一个实例,所有线程共享一个实例。
二、实现单例的方式
1、饿汉模式:类初始化的时候,静态资源全部被初始化,所以单例类的唯一实例就被创建出来了。该模式下不管你用不用,该类被初始化就会创建唯一实例。
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉饿汉模式
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfHungry extends Thread{
private static SingletonOfHungry singletonOfHungry = new SingletonOfHungry();
SingletonOfHungry(){
}
public static SingletonOfHungry getSingletonOfHungry() {
return singletonOfHungry;
}
public static void main(String[] args) {
List<Thread> list = Lists.newArrayList();
for(int i=0;i<10;i++){
list.add(new Thread(()-> System.out.println(SingletonOfHungry.getSingletonOfHungry().hashCode())));
}
list.forEach((thread)->thread.start());
}
}
2、懒汉模式:类初始化的时候,静态资源全部被初始化为null,只有调用另一个静态方法的时候单例类的唯一实例。
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉懒汉模式 synchronized关键字和同步代码块
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfLazy {
private static SingletonOfLazy singletonOfLazy = null;
private SingletonOfLazy() {
}
//由于懒汉模式需要建立对象,可能由于准备工作耗时,所以需要同步。不然会出现线程安全问题。
//但是synchronized锁方法效率很低,我们应该用同步块来实现
// public static synchronized SingletonOfLazy getSingletonOfLazy() {
public static SingletonOfLazy getSingletonOfLazy() {
synchronized (SingletonOfLazy.class) {
if (singletonOfLazy == null) {
try {
//模拟生成对象耗时300ms
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
singletonOfLazy = new SingletonOfLazy();
}
}
return singletonOfLazy;
}
public static void main(String[] args) {
List<Thread> list = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
list.add(new Thread(() -> System.out.println(SingletonOfLazy.getSingletonOfLazy().hashCode())));
}
list.forEach((thread) -> thread.start());
}
}
3、双重检查加锁机制:由于懒汉虽然实现了懒加载,但是加锁,用时间换取空间的代价太大。而该机制只需要再第一次创建单例对象的时候锁住。大大减少了锁对时间的消耗。
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉双重检查加锁机制
* 既保证了其高效性,也保证了其线程安全性
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfLazy2 {
volatile private static SingletonOfLazy2 singletonOfLazy2 = null;
private SingletonOfLazy2() {
}
public static SingletonOfLazy2 getSingletonOfLazy2() {
if (singletonOfLazy2 == null) {
try {
//模拟生成对象耗时300ms
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (SingletonOfLazy2.class) {
if (singletonOfLazy2 == null) {
singletonOfLazy2 = new SingletonOfLazy2();
}
}
}
return singletonOfLazy2;
}
public static void main(String[] args) {
List<Thread> list = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
list.add(new Thread(() -> System.out.println(SingletonOfLazy2.getSingletonOfLazy2().hashCode())));
}
list.forEach((thread) -> thread.start());
}
}
4、类级内部类(静态内部类)实现模式:
(1)、利用外部类实例化时,静态内部类在没被调用时是不会被初始化的
(2)、利用缺省锁机制,比如:
1.由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
2.访问final字段时
3.在创建线程之前创建对象时
4.线程可以看见它将要处理的对象时
在这些情况下,JVM已经隐含地执行了同步。我们可以利用她。
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉静态内部类实现单例模式
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfStaticInnerClass {
private static class SingletonHandler {
private static SingletonOfStaticInnerClass singletonOfStaticInnerClass = new SingletonOfStaticInnerClass();
}
private SingletonOfStaticInnerClass() {
}
private static SingletonOfStaticInnerClass getSingleton() {
return SingletonHandler.singletonOfStaticInnerClass;
}
public static void main(String[] args) {
List<Thread> list = Lists.newArrayList();
for (int i = 0; i < 10; i++) {
list.add(new Thread(() -> System.out.println(SingletonOfStaticInnerClass.getSingleton().hashCode())));
}
list.forEach((thread) -> thread.start());
}
}
5、枚举实现模式(简洁,好用,推荐)
代码如下:
按照《高效Java 第二版》中的说法:单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public enum SingletonOfEnumFactory {
/**
* 单例元素
*/
SingletonInstance("name", "wangzha");
private String key;
private String value;
private SingletonOfEnumFactory(String key, String value) {
this.key = key;
this.value = value;
}
}
6、序列化和反序列化的单例模式
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉序列化与反序列化的单例模式实现
* 序列号对象的hashCode和反序列化后得到的对象的hashCode值不一样,说明反序列化后返回的对象是重新实例化的,单例被破坏了。
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfStaticInnerClass2 implements Serializable {
private static final long serialVersionUID = 1L;
SingletonOfStaticInnerClass2() {
}
private static class SingletonHandler {
private static SingletonOfStaticInnerClass2 singletonOfStaticInnerClass2 = new SingletonOfStaticInnerClass2();
}
public static SingletonOfStaticInnerClass2 getSingletonOfStaticInnerClass2() {
return SingletonHandler.singletonOfStaticInnerClass2;
}
public static void main(String[] args) {
SingletonOfStaticInnerClass2 singletonOfStaticInnerClass2 = getSingletonOfStaticInnerClass2();
File file = new File("singletonOfStaticInnerClass2.txt");
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(singletonOfStaticInnerClass2);
System.out.println(singletonOfStaticInnerClass2.hashCode());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
fileInputStream = new FileInputStream(file);
objectInputStream = new ObjectInputStream(fileInputStream);
try {
singletonOfStaticInnerClass2 = (SingletonOfStaticInnerClass2) objectInputStream.readObject();
System.out.println(singletonOfStaticInnerClass2.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
采用下面的代码来解决上面代码遇到的问题
代码如下:
/**
* 〈一句话功能简述〉<br>
* 〈功能详细描述〉反序列化的过程中使用readResolve()方法
*
* @author wangzha
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class SingletonOfStaticInnerClass3 implements Serializable {
private static final long serialVersionUID = 1L;
SingletonOfStaticInnerClass3() {
}
private static class SingletonHandler {
private static SingletonOfStaticInnerClass3 singletonOfStaticInnerClass3 = new SingletonOfStaticInnerClass3();
}
public static SingletonOfStaticInnerClass3 getSingletonOfStaticInnerClass3() {
return SingletonOfStaticInnerClass3.SingletonHandler.singletonOfStaticInnerClass3;
}
//该方法在反序列化时会被调用,该方法不是接口定义的方法,有点儿约定俗成的感觉
protected Object readResolve() throws ObjectStreamException {
System.out.println("调用了readResolve方法!");
return SingletonHandler.singletonOfStaticInnerClass3;
}
public static void main(String[] args) {
SingletonOfStaticInnerClass3 singletonOfStaticInnerClass3 = getSingletonOfStaticInnerClass3();
File file = new File("singletonOfStaticInnerClass3.txt");
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(singletonOfStaticInnerClass3);
System.out.println(singletonOfStaticInnerClass3.hashCode());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileOutputStream != null) {
fileOutputStream.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
fileInputStream = new FileInputStream(file);
objectInputStream = new ObjectInputStream(fileInputStream);
try {
singletonOfStaticInnerClass3 = (SingletonOfStaticInnerClass3) objectInputStream.readObject();
System.out.println(singletonOfStaticInnerClass3.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}