锁定老帖子 主题:面试题:线程安全的单例模式
该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-01
不知道我们这个类是不是你面试者问的线程单利类
我一个类实现Runalbe接口, 有一个BlockingQueue queue = new LinkedListBlockingQueue();(大吞吐量) 然后有一个缓存池线程池 Executer exec = Executors.newFixedThreadPool(3); 然后这个类写成单例的,private 构造器 在run方法里实现另一个县城 用exec.execute实现 /** * 任务队列 */ private static final BlockingQueue<Map> queue = new LinkedBlockingQueue<Map>(30); /** * 线程池 */ private static final ExecutorService exec = Executors.newFixedThreadPool(3); /** * 任务队列 */ private static final BlockingQueue<Map> queue = new LinkedBlockingQueue<Map>(30); /** * 线程池 */ private static final ExecutorService exec = Executors.newFixedThreadPool(3); public static synchronized DataSendMgr getInstance(){ if( instance == null ){ if( Constants.getDbAgentIsWorking() ){ instance = new DataSendMgr(); new Thread(instance).start(); } return instance; } public void run() { Map execMap = null; while(runFlag){ try { try { execMap = queue.take(); if( STOP_FLAG_OBJECT.equals(execMap) ){ break; } exec.execute( new SendData( service.getDatagramSession(), execMap) ); } catch (SocketException e) { service.quit(); logger.error("SenderService Error::" + MethodUtil.printExceptionErr(e)); } } catch (InterruptedException e) { logger.error("DataSendMgr Thread take Object from queue Error::" + e.getMessage()); } execMap = null; } if( logger.isInfoEnabled() ){ logger.info( "DataSendMgr Thread stop running..." ); } } 一部分代码! 总之这个线程是单例的,然后由线程池来开启另一个子线程 |
|
返回顶楼 | |
发表时间:2009-12-01
aniu2008 写道 Hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下
1. public static final ThreadLocal session = new ThreadLocal(); 2. public static Session currentSession() { 3. Session s = (Session)session.get(); 4. //open a new session,if this session has none 5. if(s == null){ 6. s = sessionFactory.openSession(); 7. session.set(s); 8. } return s; 9. } 我们逐行分析 1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。 如果不初始化initialvalue,则initialvalue返回null。 3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。 5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。 6。创建一个数据库连接实例 s 7。保存该数据库连接s到ThreadLocal中。 8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。 LZ这个也没谈到Hibernate 你们干嘛要说Hibernate中的ThreadLocal啊 - - 崩溃啊啊!!!!! 就是一个管理Sessin的类 |
|
返回顶楼 | |
发表时间:2009-12-01
fangin 写道 tomorrow009 写道 Singleton模式分两种,“懒汉”和“恶汉” 恶汉模式也就是前面很多人提到的
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){ //私有化构造函数. }; public static Singleton getInstance(){ return instance; } } 由于实例在类加载时就已经创建,所以不存在线程同步问题,如果该Singleton初始化时不需要很大的开销(比如io操作/数据库连接)之类的,通常用这种办法就可以了. 某些情况下我们希望实例只有被第一次用到的时候才创建,那么这时候就使用“懒汉”模式 第一种方式(加载时创建实例)有什么问题?类的加载机制是第一次调用static方法时类才加载,也就是在调用getInstance()方法时Singleton才被加载,这样跟第二种的加载时间有什么不同?不都是第一次使用时才加载吗? 以上是我的困惑,希望楼下有人给我解惑 同问。 只在调用静态方法getInstance时,instance 才会初始化。至少在JDK1.6是如此的。又何必去再里面定义一个内部类,加一个静态变量,在getInstance方法里面调用此变量呢。很疑惑。 PS 越看越觉得书读的少啊。。。 |
|
返回顶楼 | |
发表时间:2009-12-01
最后修改:2009-12-01
香克斯 写道 bencode 写道 keshin 写道 public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } } java concurrency in practice中建议的方式 这个好, 延迟初始化,线程安全,效率高(没有使用同步锁,而由类加载器保证) 简洁 凤舞凰杨都跟你们说了多看看书,还是有人搞出什么double check啊之类的来. 上面这个是目前最简单有效的单例方式. 就是一堆人视而不见 是的上面是最简单有效的 Lazy Loading Singletons实现方法,关于几种Singleton实现方法,Google工程师Bob Lee有个很好的帖子 http://crazybob.org/2007/01/lazy-loading-singletons.html。 总结成以下三点: 1. 使用Synchronized同步getInstance方法, 简单有效适合所有的JVM版本,但Lock contention带来性能开销 2. 使用Double-checked Locking 和只同步create instance的部分,同时必须声明单列变量为volatile,否则同样不是完全线程安全的。同样由于Java Memory Model的对volatile的模糊定义,这个模式无法使用在5之前的JVM。新的JMM对volatile定义更明确,compound operation (比如++, get-set)也是原子性的,所以DCL可以放心使用在Java 5中。使用在5以后版本,可以提升10%性能(bob lee测试) 3. 最快的方法还是Lazy Loading Singletongs, 它从Initialization on Demand Holder (IODH) 模式演化而来, 针对这个模式Effective Java 第48条也有很详细的描述。 最后还是要看情况来合理使用各种技巧, 很多时候其实最老土的发法一还是很好很管用的 |
|
返回顶楼 | |
发表时间:2009-12-01
最后修改:2009-12-01
香克斯 写道 bencode 写道 keshin 写道 public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } } java concurrency in practice中建议的方式 这个好, 延迟初始化,线程安全,效率高(没有使用同步锁,而由类加载器保证) 简洁 凤舞凰杨都跟你们说了多看看书,还是有人搞出什么double check啊之类的来. 上面这个是目前最简单有效的单例方式. 就是一堆人视而不见 我也看过developerworks上讨论的double-check的问题。但据说那个问题在jdk1.5已经解决了。http://zhangle.iteye.com/blog/259991 应该可以像http://www.ibm.com/developerworks/cn/java/j-dcl.html中所说的那样分析出来的,但我电脑上目前没安装Visual Studio,先留言在这,回去试出来了再说。 ……楼上已经说了。 |
|
返回顶楼 | |
发表时间:2009-12-01
不太懂啊!
|
|
返回顶楼 | |
发表时间:2009-12-01
xprayc 写道 通常有两种常见的策略实现单例,一如lz所言,即所谓lazy形式的。如果害怕线程安全问题,而又不想用synchronized影响性能的话,不如用另一种:
public class Singleton { private Singleton() {} // 载入class时立即初始化 private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } 当然这种也有缺点,instance会立即初始化,而不管是否实际用到。:) 这个缺点根本就不是缺点。你用不到这个类为什么访问它呢,访问它获得实例必须初始化。说白了这个所谓缺点仔细想明白,你会发现这是最佳实现方式。 |
|
返回顶楼 | |
发表时间:2009-12-01
keshin 写道 public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } } java concurrency in practice中建议的方式 又学习了单例模式一种新的写法 |
|
返回顶楼 | |
发表时间:2009-12-02
最后修改:2009-12-02
还有一种:
public class Something { private Something() { } private static class LazyHolder { private static final Something something = new Something(); } public static Something getInstance() { return LazyHolder.something; } } 原理参见 http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom |
|
返回顶楼 | |
发表时间:2009-12-05
没想到单例模式如此复杂,上次考试时幸好没有在这上面刁难 哈哈
|
|
返回顶楼 | |