1.  Java之单例模式(Singleton Pattern )

单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。

  单例模式有一下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

  正是由于这个特点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证其他对象读到一致的信息。例如在某个服务器程序中,该服务器的配置信息可能存放在数据库或 文件中,这些配置数据由某个单例对象统一读取,服务进程中的其他对象如果要获取这些配置信息,只需访问该单例对象即可。这种方式极大地简化了在复杂环境 下,尤其是多线程环境下的配置管理,但是随着应用场景的不同,也可能带来一些同步问题。

2.Java单例模式3种写法:

(1)懒汉:(用的时候,才去创建对象)

 1 public class Singleton {
2 private static Singleton instance;
3 private Singleton (){}
4 public static Singleton getInstance() {
5 if (instance == null) {
6 instance = new Singleton();
7 }
8 return instance;
9 }
10 }
11

致命的是在多线程不能正常工作,线程不安全。

以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,对getInstance这个方法改造有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全:

优化懒汉,实现线程安全。做法如下3种:

        •在getInstance方法上加同步:     

public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}

•双重检查锁定:

public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}

   •静态内部类:

public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return
LazyHolder.INSTANCE;
}
}

附加:懒汉式Teacher类案例:

  •Teacher类:

 package cn.itcast_03;

 /*
* 面试:懒汉式(可能会出问题的单例模式)
* A:懒加载(延迟加载)
* B:线程安全问题
* a:是否多线程环境 是
* b:是否有共享数据 是
* c:是否有多条语句操作共享数据 是
*/
public class Teacher {
private Teacher() {
} private static Teacher t = null; public synchronized static Teacher getTeacher() {
// t1,t2,t3
if (t == null) {
//t1,t2,t3
t = new Teacher();
}
return t;
}
}

  •Teacher测试类:

 package cn.itcast_03;

 public class TeacherDemo {
public static void main(String[] args) {
Teacher t1 = Teacher.getTeacher();
Teacher t2 = Teacher.getTeacher();
System.out.println(t1 == t2);
System.out.println(t1); // cn.itcast_03.Teacher@175078b
System.out.println(t2);// cn.itcast_03.Teacher@175078b
}
}

(2)饿汉:(类一加载就创建对象)

//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return
single;
}
}

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

其实饿汉还有变种编写方式如下:

 1 public class Singleton1 {
2 private Singleton1 instance = null;
3 static {
4 instance = new Singleton1();
5 }
6 private Singleton1 (){}
7 public static Singleton1 getInstance() {
8 return this.instance;
9 }
10 }
11

表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。

(3)登记式单例(可忽略)

//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if(name == null) {
name = Singleton3.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null
);
System.out.println(single3.about());
}
}

       

        登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

(4)枚举实现单例模式:

        单例模式约束一个类只能实例化一个对象。在Java中,为了强制只实例化一个对象,最好的方法是使用一个枚举量。这个优秀的思想直接源于Joshua Bloch的《Effective Java》(《Java高效编程指南》)。如果你的藏书室里还没有这本书,请搞一本,它是迄今为止最优秀的Java书籍之一。

这里有几个原因关于为什么在Java中宁愿使用一个枚举量来实现单例模式:

       ♦ 自由序列化;

        保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);

       ♦ 线程安全;

案例:

 public enum AnimalHelperSingleton {

     INSTANCE;

     private AnimalHelperSingleton(){

     }

     public Animal[] buildAnimalList(){
final Animal[] animals = new Animal[10]; animals[0] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
"Dog", true, Color.GRAY);
animals[1] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
"Cat", true, Color.YELLOW);
animals[2] = new SimpleAnimal(Animal.AnimalClass.AMPHIBIAN,
"Frog", true, Color.GREEN);
animals[3] = new SimpleAnimal(Animal.AnimalClass.BIRD,
"Crow", true, Color.BLACK);
animals[4] = new SimpleAnimal(Animal.AnimalClass.BIRD,
"Cardinal", true, Color.RED);
animals[5] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD,
"Mantis", false, Color.GREEN);
animals[6] = new SimpleAnimal(Animal.AnimalClass.ARTHROPOD,
"Spider", false, Color.ORANGE);
animals[7] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
"Tiger", true, Color.ORANGE);
animals[8] = new SimpleAnimal(Animal.AnimalClass.MAMMAL,
"Bear", true, Color.BLACK);
animals[9] = new SimpleAnimal(Animal.AnimalClass.BIRD,
"Owl", true, Color.BLACK); return animals;
} }

如何使用:

//Call singleton to build the animal list.
Animal[] animals = AnimalHelperSingleton.INSTANCE.buildAnimalList();

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

3.小结:

(1)饿汉式和懒汉式区别:

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:         

 ->1、线程安全:

                      饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

                 懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。

   ->2、资源加载和性能:

                          饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其                   资源已经初始化完成,而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能                   上会有些延迟,之后就和饿汉式一样了。

 

(2)什么是线程安全:

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

4. 面试题:单例模式的思想是什么?请写一个代码体现。

答:单例模式的思想:客户端不再需要考虑是否需要去实例化的问题,而把责任都给了应该负责的类去处理。保证一个类仅有一个实例,并提供一个访问它的全局访问点。

   开发:饿汉式(是不会出问题的单例模式)
   面试:懒汉式(可能会出问题的单例模式)
     A:懒加载(延迟加载)
     B线程安全问题
       a是否多线程环境   是
       b是否有共享数据   是
       c是否有多条语句操作共享数据   是

代码实现:

• 老师类:

 package cn.itcast_03;

 public class Teacher {
private Teacher() {
} private static Teacher t = null; public synchronized static Teacher getTeacher() {
// t1,t2,t3
if (t == null) {
//t1,t2,t3
t = new Teacher();
}
return t;
}
}

• 测试类:

 package cn.itcast_03;

 public class TeacherDemo {
public static void main(String[] args) {
Teacher t1 = Teacher.getTeacher();
Teacher t2 = Teacher.getTeacher();
System.out.println(t1 == t2);
System.out.println(t1); // cn.itcast_03.Teacher@175078b
System.out.println(t2);// cn.itcast_03.Teacher@175078b
}
}

Java设计模式03:常用设计模式之单例模式(创建型模式)的更多相关文章

  1. (转)Java经典设计模式(1):五大创建型模式(附实例和详解)

    原文出处: 小宝鸽 一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代 ...

  2. 单例模式——创建型模式01

    1. 名称     单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式是一种对象创建型模式. 2. 问题    ...

  3. [C#]设计模式-单例模式-创建型模式

    单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...

  4. Sington单例模式(创建型模式)

    一.使用Sington单例模式的动机(Motivation) 在软件系统中,经常有一些特殊的类,必须保证它们只有一个实例,才能保证它的逻辑正确性.以及良好的效率. 大多数类用的是常规的构造器,所以往往 ...

  5. Java设计模式 - 单例模式(创建型模式)

    单例模式我在上学期看一些资料时候学习过,没想到这学期的软件体系结构就有设计模式学习,不过看似篇幅不大,介绍得比较简单,在这里我总结下单例模式,一来整理之前的笔记,二来也算是预习复习课程了. 概述 单例 ...

  6. java架构之路-(设计模式)五种创建型模式之单例模式

    设计模式自身一直不是很了解,但其实我们时刻都在使用这些设计模式的,java有23种设计模式和6大原则. 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可 ...

  7. Java设计模式——单例模式(创建型模式)

    概述   单例模式保证对于每一个类加载器,一个类仅有一个实例并且提供全局的访问.其是一种对象创建型模式.对于单例模式主要适用以下几个场景: 系统只需要一个实例对象,如提供一个唯一的序列号生成器 客户调 ...

  8. Java 23种设计模式详尽分析与实例解析之一--创建型模式

    面向对象的设计原则 常用的面向对象设计原则包括7个,这些原则并不是独立存在的,它们相互依赖.互为补充. Java设计模式 创建型模式 简单工厂模式 模式动机: 考虑一个简单的软件应用场景,一个软件系统 ...

  9. Java开发中的23种设计模式详解(1)创建型

    设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  10. Java设计模式 --- 七大常用设计模式示例归纳

    设计模式分为三种类型,共23种: 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式 结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式 行为型模式:模 ...

随机推荐

  1. Java Fluent Restful API自动化测试框架

    这是一个Restful API自动化测试框架,这是一个能让你写出高可读性测试代码的测试框架! 项目目标 话说目前行业内,Restful API自动化测试框架已经不是稀罕物了,各个语言都有自己的实现机制 ...

  2. [译]36 Days of Web Testing(五)

    Day 23 禁用CSS  Disable CSS 为什么 ? CSS,层叠样式表,是用来定义web页面布局和显示的机制.通过修改CSS样式,可以改变整个页面的外观. 但是有一些人,因为之前的选择或者 ...

  3. 转:靠谱的代码和DRY

    http://www.cppblog.com/vczh/archive/2014/07/15/207658.html 靠谱的代码和DRY 上次有人来要求我写一篇文章谈谈什么代码才是好代码,是谁我已经忘 ...

  4. Unity3d 物理 Rigidbody预防穿插

    Unity3d 物理 Rigidbody预防穿插 @广州小龙 Unity的物理引擎采用的是Physx引擎,里面的组件是Rigidbody!这个可以模拟物理效果! Rigidbody,这个是可以进行穿插 ...

  5. CPU再烂,俺也支持虚拟化呀,再附送64位WINDOWS的IIS上配置支持PHP的注意事项

    原来要对IIS进行降权,让他可以支持32位程式     cscript.exe %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/App ...

  6. Hibernate 注解 字段不映射的注解

    在字段前面加这个注解:@Transient

  7. 【HDOJ】2988 Dark roads

    最小生成树. /* */ #include <iostream> #include <string> #include <map> #include <que ...

  8. 【HDOJ】3367 Pseudoforest

    并查集. #include <cstdio> #include <cstring> #include <cstdlib> #define MAXN 10005 #d ...

  9. RHCS集群理论暨最佳实践

    RHCS集群理论暨 最佳实践 什么是集群?     集群是一组(>2)相互独立的,通过高速网络互联的计算机组成的集合.群集一般可以分为科学集群,负载均衡集群,高可用性集群三大类.     科学集 ...

  10. 【狼】unity3d collision获取碰撞的点的位置

    void OnCollisionEnter(Collision ctl) { ContactPoint contact = ctl.contacts[]; Quaternion rot = Quate ...