论坛首页 入门技术论坛

面试题:线程安全的单例模式

浏览 35855 次
该帖已经被评为新手帖
作者 正文
   发表时间: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..." );
		}
	}



一部分代码! 总之这个线程是单例的,然后由线程池来开启另一个子线程
0 请登录后投票
   发表时间: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的类
0 请登录后投票
   发表时间: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 越看越觉得书读的少啊。。。
0 请登录后投票
   发表时间: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条也有很详细的描述。

最后还是要看情况来合理使用各种技巧, 很多时候其实最老土的发法一还是很好很管用的




0 请登录后投票
   发表时间: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,先留言在这,回去试出来了再说。

……楼上已经说了。
0 请登录后投票
   发表时间:2009-12-01  
不太懂啊!
0 请登录后投票
   发表时间: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会立即初始化,而不管是否实际用到。:)


这个缺点根本就不是缺点。你用不到这个类为什么访问它呢,访问它获得实例必须初始化。说白了这个所谓缺点仔细想明白,你会发现这是最佳实现方式。
0 请登录后投票
   发表时间: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中建议的方式



又学习了单例模式一种新的写法
0 请登录后投票
   发表时间: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
0 请登录后投票
   发表时间:2009-12-05  
没想到单例模式如此复杂,上次考试时幸好没有在这上面刁难 哈哈
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics