`
Damon_Zhang
  • 浏览: 71284 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java 单例模式及运用

阅读更多

定义:

确保某一个类只有一个实例,而且自动实例化并向整个系统提供这个实例。

代码:

Singleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化的。

/**
 * 线程安全的单例模式
 * 饿汉式单例
 * @author Administrator
 *
 */
public class Singleton {
     
    private static final Singleton singleton = new Singleton();
     
    // 限制产生多个对象
    private Singleton() {
    }
     
    // 通过该方法获得实例对象
    public static Singleton getSingleton() {
        return singleton;
    }
     
    // 类中其他方法尽量是static
    public static void doSomething() {
         
    }
}

 

单例模式的优点:

  1. 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显了。
  2. 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
  3. 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
  4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

 

单例模式的缺点:

  1. 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
  2. 单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能用mock的方式虚拟一个对象。
  3. 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把“要单例”和业务逻辑融合在一个类中。

 

单例模式的使用场景:

  1. 要求生成唯一序列号的环境;
  2. 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
  3. 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  4. 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。

 

单例模式的注意事项:

  1.  在高并发情况下,请注意单例模式的线程同步问题。

/**
 * 懒汉式单例
 * @author Administrator
 *
 */
public class Singleton2 {
 
    private static Singleton2 singleton = null;
 
    // 限制产生多个对象
    private Singleton2() {
    }
 
    // 通过该方法获得实例对象
    public static Singleton2 getSingleton() {
        synchronized (singleton) {
            if (singleton == null) {
                singleton = new Singleton2();
            }
        }
        return singleton;
    }
}

 

如果不加synchronized进行控制,如果第一个线程A执行到singleton = new Singleton2(),但还没有获得对象,第二个线程B也在执行,执行到if (singleton == null)判断,那么线程B获得判断条件为真,于是继续运行下去,线程A和B都获得了对象,内存中就出现了两个对象。

建议使用饿汉式单例,那是线程安全的单例模式。

 

  2.  需要考虑对象的复制情况。在Java中,若实现Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制是不用调用类的构造函数的。

 

  3.  注意JVM的垃圾回收机制,如果我们的一个单例对象在内存中长久不使用,JVM就认为这是一个垃圾对象,在CPU资源空闲的情况下该对象会被清理掉,下次再调用时就需要产生一个新对象。如果该对象作为有状态值的管理,则会出现状态恢复原状的情况,就会出现故障。

有两种方法可以解决该问题

1、由容器管理单例的生命周期

Java EE容器或者框架级容器(如Spring)可以让对象长久驻留内存。

2、状态随时记录

可以使用异步记录的方式,或者使用观察者模式,记录状态的变化,写入文件或写入数据库中,确保即使单例对象重新初始化也可以从资源环境获得销毁前的数据,避免应用数据丢失。

 

单例模式的扩展:

能产生固定数量实例的单例模式

/**
 * 能产生固定数量实例的单例模式
 * @author Administrator
 *
 */
public class Singleton3 {
 
    // 最多能产生的实例数
    private static int maxNumOfSingleton = 2;
 
    // 定义一个列表,容纳所有实例
    private static ArrayList<Singleton3> singletonList = new ArrayList<Singleton3>();
 
    // 产生所有对象
    static {
        for (int i = 0; i < maxNumOfSingleton; i++) {
            singletonList.add(new Singleton3());
        }
    }
 
    // 限制其他类生成对象
    private Singleton3() {
    }
 
    // 随机获得一个实例
    public static Singleton3 getInstance() {
        Random random = new Random();
        return singletonList.get(random.nextInt(maxNumOfSingleton));
    }
 
}

 在SSH中的运用

1.hibernate 中

我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,采用前面提到的“饿汉模式”创建单例。多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?以上代码使用ThreadLocal模式的解决了这一问题。 只要借助上面的工具类获取Session实例,我们就可以实现线程范围内的Session共享,从而避免了线程中频繁的创建和销毁Session实例。当然,不要忘记在用完后关闭Session。
ThreadLocal和线程同步机制相比有什么优势呢?
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单、更方便,且结果程序拥有更高的并发性。ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。
不过在使用线程池的情况下,使用ThreadLocal应该慎重,因为线程池中的线程是可重用的。
ThreadLocal的内容以后还要仔细学习一下。

 2.spring中

项目加载的时候bean(一个bean对应某个类)自动创建(初始化,建一个实例),而后是每次调用bean的时候是注入的(不是重新new。。所有整个系统都是这个实例,,而且是spring自动提供的)


一:对于Spring中实现Singleton模式,是以IOC容器为单位(就是说在这个容器里面bean实现Singleton),换句话说,一个JVM可能有多个IOC容器,而在这个容器里实现了singleton bean,
而Java中实现Singleton模式而言,只有一个JVM,jvm中某个class只有一个实例
二:
singleton模式中,singleton的class在整个JVM中只有一个instance,
Spring的Bean,你可以一个class配置多个Bean,这个class就有了多个instance。

这个singleton是指在spring容器中,这个Bean是单实例的,是线程共享的。所以要求这些类都是线程安全的。也就是说,不能出现修改Bean属性的方法,当然除了设值得那些setter。只要满足线程安全,这些bean都可以用singleton。而且我们在绝大多数使用上,也是这样用的,包括dao,service。
Beanfactory是Spring初始以静态方式载入的,Spring的单例IOC是基于容器级的,所以这你都不用担心与考虑.

 3.struts

" Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
" Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)

Action执行的控制:
" Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
" Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。

关注微信公众账号: 快乐编程  ,一起交流技术知识,享受快乐编程!

2
7
分享到:
评论
5 楼 Damon_Zhang 2013-07-26  
jjhpeopl 写道
内容不错,就是spring中的bean,对于每个线程都是用的threadlocal吗?

Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了
4 楼 jjhpeopl 2013-07-26  
内容不错,就是spring中的bean,对于每个线程都是用的threadlocal吗?
3 楼 Damon_Zhang 2013-07-25  
jahu 写道
说的全是废话,,,请你认真的研究下,,
    你把方法全静态话,还需要单例模式干什么。。这么基础的东西,都没弄明白,,,你别误导人行不。

1.单例模式就是防止多次创建实例,静态方法不可以被重写,因此能很好的控制,但是这也是单例模式的缺点,那就是不易扩展,请参考单例模式优缺点;
2.请注意看代码,单例模式中实例化的方法即getInstance()是静态的,其他方法不一定是静态的。
3.请你多去了解一下单例模式的相关文章和应用;
2 楼 jahu 2013-07-25  
说的全是废话,,,请你认真的研究下,,
    你把方法全静态话,还需要单例模式干什么。。这么基础的东西,都没弄明白,,,你别误导人行不。
1 楼 freezingsky 2013-07-25  
虽然内容基本都是看过的,但至少放在一起,算比较综合。

相关推荐

    java单例模式实例

    单例模式是软件设计模式中的一种经典模式,用于确保一个类只有一个实例,并提供一个全局访问点。...通过学习和实践这些单例模式的实现,我们可以更好地理解和应用设计模式,提升代码的质量和可维护性。

    Java 单例模式.pptx

    #### 三、单例模式的创建方式及优缺点 单例模式的实现方式主要有三种:饿汉式、懒汉式以及静态内部类方式。 - **饿汉式** - **实现**: ```java public class Single1 { // 创建一个静态的实例 private ...

    java单例模式及实现

    Java单例模式及实现 Java单例模式是一种常见的设计模式,确保某一个类只有一个实例,而且向这个系统提供这个实例。单例模式可以分为三种:懒汉式单例、饿汉式单例、登记式单例。 单例模式的要点 1. 某个类只能有...

    Java单例模式应用研究.pdf

    ### Java单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最基本的创建型设计模式,其主要目的是控制一个类的实例化过程,确保在整个应用程序中仅存在一个实例,并且该实例能够被全局...

    使用Java单例模式实现一个简单的日志记录器.txt

    ### 使用Java单例模式实现一个简单的日志记录器 #### 一、单例模式简介 单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括...

    JAVA单例模式的几种实现方法

    ### JAVA单例模式的几种实现方法 #### 一、饿汉式单例类 饿汉式单例类是在类初始化时就已经完成了实例化的操作。这种实现方式简单且线程安全,因为实例化过程是在编译期间完成的,不会受到多线程的影响。 **代码...

    Java单例模式深入理解

    Java单例模式是一种设计模式,它允许在程序中创建唯一一个类实例,通常用于管理共享资源,例如数据库连接、线程池或者配置对象等。单例模式的核心在于限制类的构造函数,确保类只能被初始化一次,从而实现全局唯一的...

    java单例模式在android中的应用

    Java的单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供全局访问点。在Android开发中,单例模式的应用尤其广泛,因为它能够有效管理资源,减少内存开销,提高性能。以下是对单例模式在Android中...

    设计模式单例模式和工厂模式综合应用

    **代码**文件则提供了实际的Java实现,通过查看这些代码,我们可以看到单例模式和工厂模式如何在实践中被运用,包括类的定义、方法的实现以及如何通过代码调用来创建和管理对象。 总的来说,这个项目为学习和理解...

    java单例模式连接数据库源码

    Java单例模式是一种设计模式,它保证一个类...总的来说,这个Java单例模式的数据库连接源码提供了一种简单但有效的数据库连接管理方式,适合小规模应用。然而,在大型项目中,建议采用连接池来管理和优化数据库连接。

    java单例模式的例子

    Java单例模式是一种常见的设计模式,它在软件工程中用于控制类的实例化过程,确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统资源管理、缓存、日志记录等方面应用广泛。下面我们将深入探讨Java单例...

    Java设计模式之单例模式

    目录 单例模式的概念 单例模式的要点 单例模式类图 单例模式归类 单例模式的应用场景 单例模式解决的问题 单例模式的实现方式 单例模式实现方式对比 单例模式的概念 单例模式,顾名思义就是只有一个实例,并且由它...

    java设计模式之单例模式.zip

    Java设计模式是面向对象编程中的重要概念,它们是软件开发中经过验证的、解决常见问题的最佳实践。...观看这些视频,可以更深入地理解并掌握Java中的单例模式,从而在实际开发中灵活运用,提升代码质量。

    详解JAVA单例模式及多种实现.doc

    以下是几种常见的Java单例模式实现方式: 1. **饿汉式(静态常量)**: 这种方式在类加载时即初始化实例,线程安全,但可能导致不必要的内存占用。 ```java public class Singleton1 { private Singleton1() {} ...

    java单例模式代码实例

    总结,Java单例模式在软件设计中有着广泛应用,理解并合理运用不同的实现方式,可以帮助我们更好地控制对象的生命周期,提高系统性能。在实际开发中,需要根据项目需求选择合适的单例实现方式,并注意其可能带来的...

    Java单例模式与工厂模式简单示例代码

    在Java中,单例模式的实现通常有几种方法: 1. **饿汉式(静态常量)**:在类加载时就完成初始化,所以类加载比较慢,但获取对象的速度快,且线程安全。 ```java public class Singleton { private static final ...

    单例模式单例模式单例模式

    单例模式是软件开发中最基本的设计模式之一,尤其在Java编程中应用广泛。它的核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。这种模式特别适用于那些只需要一个实例就可以满足系统需求的情况...

Global site tag (gtag.js) - Google Analytics