设计模式之单例模式:

一.单例模式实现特点:①单例类在整个应用程序中只能有一个实例(通过私有无参构造器实现);②单例类必须自己创建这个实例并且可供其他对象访问(通过静态公开的访问权限修饰的getInstance()方法实现);

简单实例1:

 public class Singleton {
     private static Singleton uniqueInstance = null;
     /**
      * Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,
      * Singleton的唯一实例只能通过getInstance()方法访问。
      */
     private Singleton() {
         super();
     }

     /**
      * getInstance()方法特点:
      * ①public修饰
      * ②static修饰
      * ③在其内部创建本类对象并赋给成员变量
      */
     public static Singleton getInstance() {
         if ( uniqueInstance == null ) {
             uniqueInstance = new Singleton();
         }
         return uniqueInstance;
     }
 }

简单实例2:

①我们先在java工程中创建一个单例类

 public class Tools {
     private String name;
     private static Tools toolsInstance=null;

     private Tools(){
     }

     public String getName() {
         return name;
     }

     public void setName(String name) {
         this.name = name;
     }

     public static Tools getInstance(){
         if(toolsInstance==null){
             toolsInstance=new Tools();
         }
         return toolsInstance;
     }
 }

②我们创建一个简单的测试类,验证单例设计特点是否正确

 public class TestTools extends AndroidTestCase {
     public static void main(String[] args) {
         Tools tool1 = Tools.getInstance();
         tool1.setName("猪八戒");
         System.out.println(tool1.getName());
         Tools tool2 = Tools.getInstance();
         tool2.setName("孙悟空");
         System.out.println(tool2.getName());
         System.out.println(tool1.getName());
         if ( tool1 == tool1 ) {
             System.out.println("tool1和tool1是同一个对象");
         }
     }
 }

二.单例模式的应用扩展

1.懒汉式模式:懒汉式是典型的用时间换空间,也就是每次获取实例都会进行判断,,看是否需要创建实例,因此浪费了一定的程序运行时间,但因此避免了不必要的内存空间的占用。

2.饿汉式模式:饿汉式是典型的用空间换时间,当类加载的时候会创建类实例,故节省了运行时间,但可能浪费了不必要的内存空间。

3.登记式模式:登记式实际对一组单例模式进行的维护,主要是在数量上的扩展,通过map我们把单例存进去,这样在调用时,先判断该单例是否已经创建,是的话直接返回,不是的话创建一个登记到map中,再返回。对于数量又分为固定数量和不固定数量的。下面采用的是不固定数量的方式,在getInstance方法中加上参数(string name)。然后通过子类继承,重写这个方法将name传进去。

实例代码如下:

 public class Singleton {
     private static Map< String, Singleton > map = new HashMap< String, Singleton >();
 //静态代码块
     static {
         Singleton single = new Singleton();
         map.put(single.getClass().getName(), single);
     }
     //保护的默认构造子
     protected Singleton() {
     }
     //静态工厂方法,返还此类惟一的实例
     public static Singleton getInstance(String name) {
         if ( name == null ) {
             name = Singleton.class.getName();
             System.out.println("name == null" + "--->name=" + name);
         }
         if ( map.get(name) == null ) {
             try {
                 map.put(name, (Singleton) Class.forName(name).newInstance());
             } catch ( InstantiationException e ) {
                 e.printStackTrace();
             } catch ( IllegalAccessException e ) {
                 e.printStackTrace();
             } catch ( ClassNotFoundException e ) {
                 e.printStackTrace();
             }
         }
         return map.get(name);
     }

     public static void main(String[] args) {
         Singleton singleTest = Singleton.getInstance(null);
         System.out.println(singleTest.about());
     }
 }

补充(静态代码块和静态方法的区别):如果有些代码必须在项目启动的时候就执行,就需要使用静态代码块,这种代码是主动执行的;

需要在项目启动的时候就初始化但是不执行,在不创建对象的情况下,可以供其他程序调用,而在调用的时候才执行,这需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用。

三.线程安全:

从线程安全的角度上讲,不加同步的懒汉式是线程不安全的,比如:有两个线程,一个是线程First,一个是线程Second,他们同时调用getInstance()方法,那就可能导致并发问题,因为在First线程中判断instance==null的时候会通过new 创建单例对象,但是此时,可能在Second线程中,也进行了同样的instance==null判断,因为此时,线程First中的对象还没有创建完成,所以线程Second中也会通过new来创建单例对象,那么问题就来了!同时会创建出两个实例来,也就是说单例的作用在并发的情况下失效了。

而饿汉式是线程安全的,因为虚拟机只会装载一次,在装载类的时候是不会发生并发的。可以直接用于多线程而不会出现问题!那么,我们如何实现懒汉式的线程安全呢?很简单,我们只需要通过synchronized修饰即可解决问题,比如:

Public static synchronized Singleton getInstance(){}但是这样一来,会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式来实现呢?

双重检查加锁:

可以使用"双重检查加锁"的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。那么什么是"双重检查加锁"机制呢?

所谓双重检查加锁机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。

双重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。

示例代码如下:

 public class Singleton {
     private volatile static Singleton instance = null;
     private Singleton(){
     }
     public static Singleton getInstance(){
         //先检查实例是否存在,如果不存在才进入下面的同步块
         if(instance == null){
             //同步块,线程安全地创建实例
             synchronized(Singleton.class){
                 //再次检查实例是否存在,如果不存在才真正地创建实例
                 if(instance == null){
                     instance = new Singleton();
                 }
             }
         }
         return instance;
     }
 } 

这种实现方式可以实现既线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

Java开发之单例设计模式的更多相关文章

  1. iOS开发之单例设计模式(完整正确版本)

    单例的意思从字面上就可以略知一二,所谓单例就是确保在程序运行过程中只创建一个对象实例.可以用于需要被多次广泛或者说多次使用的资源中,比如我们常见的网络请求类.工具类以及其它管理类等.比如我iOS开发中 ...

  2. Java学习笔记——单例设计模式Singleton

    单例设计模式:singleton 解决的问题: 确保程序在运行过程中,某个类的实例instance只有一份. 特点: 1 构造函数私有化 2 自己内部声明自己 3 提供一个public方法,负责实例化 ...

  3. Java笔记(十一)……单例设计模式

    设计模式 解决某一类问题最行之有效的方法 Java中有23中设计模式 单例设计模式 解决一个类在内存中只存在一个对象 思路 将构造函数私有化 在类中创建一个本类对象 提供一个方法可以获取到对象 两种方 ...

  4. Java中常用来处理时间的三个类:Date、Calendar、SimpleDateFormate,以及Java中的单例设计模式:懒汉式、饿汉式以及静态内部类式

    (一)java.util.Date类 1.该类有一个long类型的属性:用来存放时间,是用毫秒数的形式表示,开始的日期是从1970年1月1号 00:00:00.    2.该类的很多方法都已经过时,不 ...

  5. Java面向对象_单例设计模式

    单例设计模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点 1.构造方法私有化 2.声明一个本类对象 3.给外部提供一个静态方法获取对象实例 两种实现方式:饿汉式和懒汉式 何种情况下使用呢?当 ...

  6. Java 多线程之单例设计模式

    转载:https://segmentfault.com/a/1190000007504892 概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例.饿汉 ...

  7. java中的单例设计模式

    单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供 ...

  8. Java并发-懒汉式单例设计模式加volatile的原因

    懒汉式单例的double check.例一: class SingletonClass{ private static SingletonClass instance = null; private ...

  9. 02_javaSE面试题:单例设计模式

    还记得很多年前,面试就让在白板上写个单例模式,当时技术渣渣,还写的是class A.面试官还说,你就不能写个Singleton. 面试题 编程题:写一个Singleton示例 解析 什么是Single ...

随机推荐

  1. 帝国cms后台不停的登录成功

    http://bbs.phome.net/showthread-13-305985-1.html 感谢各位的支持与帮助! e/data/adminlogin目录权限没有问题 问题在:计划任务中,刷新自 ...

  2. 一些 PHP 管理系统程序中的后门

    一些php网站管理程序的,一些后门,其实官方也没有恶意,主要是大家为了自己的安全. 我倒不怎么关心提示框,SABLOG怎么知道我的版本有漏洞呢,程序肯定有后门.每次登陆后台自动检测官方版本跟当前版本对 ...

  3. NSRange、NSPoint(CGPoint)、NSSize(CGSize)、NSRect(CGRect)

    1.NSRange: typedef struct _NSRange { NSUInteger location; NSUInteger length; } NSRange; NSRange本身是系统 ...

  4. 分布式文件系统 - FastDFS

    分布式文件系统 - FastDFS 别问我在哪里 也许我早已不是我自己,别问我在哪里,我一直在这里. 突然不知道说些什么了... 初识 FastDFS 记得那是我刚毕业后进入的第一家公司,一个技术小白 ...

  5. 作业三--Linux内核分析

    一.Linux内核源码 arch目录支持不同CPU的源代码,是内核源码中比较大的文件. fs文件系统Linux内核的源码放在kernel目录中. 二.构造一个简单的Linux系统MenuOS 三.使用 ...

  6. CGI与Servlet的区别和联系

    1. 定义: CGI(Common Gateway Interface 公共网关接口)是HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上. 2. 功能: 绝大多 ...

  7. ADT eclipse打开时出现Error: Error parsing C:\Users\admin*\.android\devices.xml

    Error: Error parsing C:\Users\admin*\.android\devices.xml  在ADT eclipse打开项目的时候出现此提示,但是又不影响使用. 原因:之前安 ...

  8. jQuery.validate errorPlacement

    在被验证的控件的后一个元素控制显示 errorPlacement: function(error, element) { element.next().css("color",&q ...

  9. linux 下 apache启动、停止、重启命令

    基本的操作方法: 本文假设你的apahce安装目录为/usr/local/apache2,这些方法适合任何情况 apahce启动命令: 推荐/usr/local/apache2/bin/apachec ...

  10. 跨线程调用控件之MethodInvoker

    原文:http://www.cnblogs.com/cm8448940/archive/2008/07/10/1240045.html 使用到两个控件,一个按钮button1,一个标签label1. ...