厨房小码农

Java-单例模式

一、名词解释

单例模式,一种常见的设计模式,在这种模式下面,单例对象的实例必须保证只有一个。
常见的线程池、缓存、日志对象等常被设计成单例。
单例模式通常具有如下特点:
  • 私有的构造方法;

  • 指向自己实例的私有静态引用;

  • 以自己实例为返回值的静态的公有方法。

、实现方式
单例模式可以根据实例化对象的时机不同,分为 懒汉式 和 饿汉式,其中懒汉式单例只有在真正使用的时候,才会实例化一个对象,并交给自己引用;饿汉式单例则是在单例类被加载的时候就实例化一个对象,并交给自己应用。
 
三、代码实例
懒汉式单例
package com.example.demo.dataStructure.designPatten.singleTon;

/**
* 单例模式-懒汉式
*/
public class SingletonDemo1 {
private static SingletonDemo1 instance = null;

private SingletonDemo1() {}

public static SingletonDemo1 getInstance() {

// 真正用到的时候在实例化
if(instance == null) {
instance = new SingletonDemo1();
}
return instance;
}

}

 

 
饿汉式单例
package com.example.demo.dataStructure.designPatten.singleTon;

/**
* 单例模式-饿汉式
*/
public class SingletonDemo2 {
private static SingletonDemo2 instance = new SingletonDemo2(); // 类加载的时候实例化

private SingletonDemo2() {}

public static SingletonDemo2 getInstance() {
return instance;
}

}

 

 
四、多线程下的单例
上面两种写法,在单线程的环境中,自然是没有问题,都能保证只有一个该类的实例。但是在多线程的环境中呢,结果又是怎么样的呢?
我们先来写个多线程的测试类
package com.example.demo.dataStructure.designPatten.singleTon;

public class Test {
public static void main(String[] args) {
// 起10个线程
for (int i = 0; i < 10; i++) {
new TestThread().start();;
}
}
}

class TestThread extends Thread {
@Override
public void run() {
int hash = SingletonDemo1.getInstance().hashCode();// 更换类名来获取不同的实例
System.out.println(hash);
}
}

 

 
饿汉式和懒汉式分别测试下,输出结果如下:
懒汉式单例:
Java-单例模式(一) - 厨房小码农 - gpf的博客
饿汉式单例:
Java-单例模式(一) - 厨房小码农 - gpf的博客
显然,懒汉式单例在多线程的情况下,不是真正意义上的单例,他不是线程安全的,而饿汉式单例,由于它在线程访问之前就已经实例化好了(该类被加载的时候,实例化,而且类在其生命周期内只能加载一次,天生的线程安全),所以它是线程安全的。
那么,懒汉式的问题出在哪里呢? 
其实,静下来分析之后,可以发现,问题就出在这一句 
if(instance == null){。。。}
试想下这样的情景:线程A和线程B都同时走到if这个判断,这是线程A突然因故挂起,线程B继续执行,初始化一个实例,这
时线程A又开始执行,他又去初始化了一个实例,显然,这是不行的。
 
解决方案其实有很多种,这里主要介绍几种本人认为效率比较高的
一、双重检验
 1 package com.example.demo.dataStructure.designPatten.singleTon;
 2 
 3 /**
 4 * 双重检验懒汉式
 5 * */
 6 public class SingletonDemo3 {
 7 private static volatile SingletonDemo3 instance = null; // volatile 关键字作用
 8 
 9 private SingletonDemo3() {}
10 
11 public static SingletonDemo3 getInstance() {
12 if(instance == null) {
13 synchronized (SingletonDemo3.class) {
14 if(instance == null) {
15 instance = new SingletonDemo3();
16 }
17 }
18 }
19 return instance;
20 }
21 
22 }

 

二、静态内部类
 1 package com.example.demo.dataStructure.designPatten.singleTon;
 2 
 3 public class SingletonDemo4 {
 4 private SingletonDemo4() {}
 5 
 6 private static class CreInstance {
 7 private static SingletonDemo4 instance = new SingletonDemo4();
 8 }
 9 
10 public static SingletonDemo4 getInstance() {
11 return CreInstance.instance;
12 } 
13 }

 

 
三、枚举
package com.example.demo.dataStructure.designPatten.singleTon;

public enum SingletonDemo5 {
INSTANCE;
}
 
这个其实可以看下这个类的.class文件就明白了。
 
 

posted on 2018-09-12 23:42  厨房小码农  阅读(198)  评论(0编辑  收藏  举报

导航