//2014.8.26 review
一个经典 DCL 问题
public class Singleton { private static Singleton instance=null; public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 if (instance == null) //2 instance = new Singleton(); //3 } } return instance; } }
这段代码有问题么?标准的double check.instance = new Singleton()根据以上几点分析,
可能执行执行了下列伪代码:
- mem = allocate(); //Allocate memory for Singleton object.
- instance = mem; //Note that instance is now non-null, but
- //has not been initialized.
- ctorSingleton(instance); //Invoke constructor for Singleton passing
- //instance.
有可能线程B在线程A完成对象的初始化之前就可能得到了对象的引用。
当线程A执行到instance = mem时,线程B 正好执行到外部的instance == null,此时,这个引用已经不为null,但是这个statnce还没有构造完成,线程B的操作立即返回使用该instance,这是不安全的。这是从操作次序被重新排序得到的分析结果,从另外happen-before的角度来看,这里多个线程操作共享变量instance之间并没有明显的happen-before关系,因此多个线程对instanc的读写可能发生不可见的情况。instance变量申明为volatile即可,既保证了可见性,又保证了操作不会被排序。然而,使用volatile来实现毕竟有性能损耗,因此如果要实现单例,完全可以避免使用DCL,而采用static方式。
例如:要解决上面提到的问题,则
public class Singleton { private static class Singleton Holder{ private static Singleton instance = new Singleton (); } public static Singleton getInstance(){ return SingletonHolder.instance ; } }
这种实现方式既保证了足够的惰性,又避免了同步或者保持可见性带来的性能损耗。
引用自http://blog.csdn.net/lovingprince/article/details/5450246
//2012.8.3
java 单例加锁方法:
ScheduleEngine是个单例类,在获得实例的方法getinstance中,两次判断其是否为空,有利于多线程的并发操作。
使得实例化时,只在第一次加锁,这样效率会有提高。
class ScheduleEngine{ private static Lock instanceLock=new ReentrantLock(); private ScheduleEngine() { setproperties; } public static ScheduleEngine getInstance(int temphandlerType) { if(null==engine) { instanceLock.lock(); try { if(null==engine) { handlerType=temphandlerType; engine=new ScheduleEngine(temphandlerType); } } finally { instanceLock.unlock(); } } return engine; } }
初始实例化 单例c3p0对象的方法,常用的是
public final class ConnectionManager { private static ConnectionManager instance; private static ComboPooledDataSource cpds; private static String c3p0Properties; /** * 从数据库连接池取连接 * @throws Exception */ private ConnectionManager() throws Exception { Properties p = new Properties(); c3p0Properties = System.getProperty("user.dir") + "/mom_project_config/database.properties"; // p.load(this.getClass().getClassLoader().getResourceAsStream(c3p0Properties)); p.load(new BufferedInputStream(new FileInputStream(c3p0Properties))); // String url = p.getProperty("url") + p.getProperty("database"); String url = p.getProperty("url") + p.getProperty("database")+"?useUnicode=true&characterEncoding=UTF-8"; cpds = new ComboPooledDataSource(); cpds.setDriverClass(p.getProperty("driverclass")); cpds.setJdbcUrl(url); cpds.setUser(p.getProperty("user")); cpds.setPassword(p.getProperty("password")); // 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 acquireIncrement cpds.setAcquireIncrement(Integer.valueOf(p .getProperty("acquireincrement"))); // 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 acquireRetryAttempts cpds.setAcquireRetryAttempts(Integer.valueOf(p .getProperty("acquireretryattempts"))); // 两次连接中间隔时间,单位毫秒。Default: 1000 acquireRetryDelay cpds.setAcquireRetryDelay(Integer.valueOf(p .getProperty("acquireretrydelay"))); // 自动提交事务;连接关闭时默认将所有未提交的操作回滚。Default: false autoCommitOnClose cpds.setAutoCommitOnClose(Boolean.valueOf(p .getProperty("autocommitonclose"))); // 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException, // 如设为0则无限期等待.单位毫秒,默认为0 cpds.setCheckoutTimeout(Integer.valueOf(p .getProperty("checkouttimeout"))); // 每多少秒检查所有连接池中的空闲连接。默认为0表示不检查。Default: 0 idleConnectionTestPeriod cpds.setIdleConnectionTestPeriod(Integer.valueOf(p .getProperty("idleconnectiontestperiod"))); // 最大空闲时间,25000秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 maxIdleTime cpds.setMaxIdleTime(Integer.valueOf(p.getProperty("maxidletime"))); // 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 initialPoolSize cpds.setInitialPoolSize(Integer.valueOf(p .getProperty("initialpoolsize"))); // 连接池中保留的最小连接数。 cpds.setMinPoolSize(Integer.valueOf(p.getProperty("minpoolsize"))); // 连接池中保留的最大连接数。Default: 15 maxPoolSize cpds.setMaxPoolSize(Integer.valueOf(p.getProperty("maxpoolsize"))); // JDBC的标准参数,用以控制数据源内加载的PreparedStatement数据.但由于预缓存的Statement属于单个Connection而不是整个连接池.所以 // 设置这个参数需要考滤到多方面的因素,如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭.默认为0; cpds.setMaxStatements(Integer.valueOf(p.getProperty("maxstatements"))); // 连接池内单个连接所拥有的最大缓存被关闭.默认为0; cpds.setMaxStatementsPerConnection(Integer.valueOf(p .getProperty("maxstatementsperconnection"))); // C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成.扩展这些操作可以有效的提升性能,通过多数程实现多个操作同时被执行.默为为3 cpds.setNumHelperThreads(Integer.valueOf(p .getProperty("numhelperthreads"))); // 用户修改系统配置参数执行前最多等待的秒数.默认为300; cpds.setPropertyCycle(Integer.valueOf(p.getProperty("propertycycle"))); // 如果设为true那么在取得连接的同时将校验连接的有效性。Default: false testConnectionOnCheckin cpds.setTestConnectionOnCheckin(Boolean.valueOf(p .getProperty("testconnectiononcheckin"))); // 因性能消耗大请只在需要的时候使用它。 // 如果设为true那么在每个connection提交的时候都将校验其有效性。 // 建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: // false testConnectionOnCheckout cpds.setTestConnectionOnCheckout(Boolean.valueOf(p .getProperty("testconnectionOncheckout"))); // 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。 // 但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。 // 如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false // breakAfterAcquireFailure cpds.setBreakAfterAcquireFailure(Boolean.valueOf(p .getProperty("breakafteracquirefailure"))); } /** * 获得ConnectionManager单例对象 * @return */ public synchronized static ConnectionManager getInstance() { if (instance == null) { try { instance = new ConnectionManager(); } catch (Exception e) { e.printStackTrace(); } } return instance; } /** * 获得连接 * @return */ public Connection getContection() { try { return cpds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; }
在初始化获得单例的方法上面加锁,不利于并发操作的执行,用第一段代码两次判断是否为空的方式,可以减少代码执行中锁的引用。 不足之处烦请朋友们指正。。
相关推荐
### 单例模式详解 #### 概述与应用场景 单例模式是一种常用的设计模式,它的主要目的是确保某个类仅有一个实例...通过上述讨论,我们可以看到单例模式在不同场景下的适用性及其潜在问题,并学习到了相应的解决方案。
第十章是Java反射机制,解释反射的基本概念,讲解反射的工作机制、主要方法和类,同时讨论反射的安全性和潜在问题。阶段项目鼓励学员通过反射进行应用程序开发。 整个课程设计系统全面,理论与实践并重,有助于学员...
最后,课程通过一个阶段项目来实践Java反射的应用,反射是Java动态性的一个体现,允许在运行时检查类的信息并动态调用方法或访问字段。 潭州软件学院的这个Java VIP系统基础学习课程全面且深入,覆盖了从入门到进阶...
《剑指Offer(第二版)》是一本针对Java程序员面试的经典书籍,涵盖了众多面试中常见的问题和编程技巧。...此外, Singleton模式也常被用来讨论单例模式的可扩展性、反序列化安全性以及枚举方式实现单例等话题。
- **懒汉式(线程安全)**:对获取实例的方法加锁,确保同一时刻只有一个线程能实例化对象,但可能导致线程阻塞。 - **双重校验锁(DCL,Double-Check Locking)**:结合了前两种方式的优点,既延迟实例化,又保证...
《剑指Offer》是程序员面试时必备的一本书籍,其内容主要是针对常见的编程面试题目进行分析,并提供Java语言的实现。修订版4作为该系列的更新,不仅包含了题目和解题思路,还对部分代码进行了重新排版,以解决原书中...
在深入探讨《HASHMAP缓存.txt》所提及的知识点前,我们先来解析一下文档的标题、描述和部分内容,以确保我们对所讨论的主题有全面的理解。标题“HASHMAP缓存.txt”暗示了文档主要关注的是Java编程语言中HashMap作为...
这种方式的好处在于,只有在首次创建实例时才需要进行同步操作,之后每次调用该方法时都不需要加锁,从而减少了不必要的同步开销。具体实现如下: ```java public class SingletonLogger { private static ...