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

静态类和单例的抉择

阅读更多
先说说我要实现的要求:我要一个Map,这个Map是常量,但是它要进行初始化,填充数据。而且这个Map要可以重用,实现优雅点的
public class ApproveMap {

	private static Map<String, String> approveCodeMap = new HashMap<String, String>();

	/**
	 * @return
	 */
	public static Map<String, String> getApprove() {
		synchronized (approveCodeMap) {
			if (approveCodeMap.isEmpty())
				setApprove();
		}
		return approveCodeMap;
	}

	private static void setApprove() {
		/**
		 *
		 */
		approveCodeMap.put("101102", "11_0003_0001");
		approveCodeMap.put("101103", "11_0003_0004");
		approveCodeMap.put("101104", "11_0003_0005");
		approveCodeMap.put("101105", "11_0003_0002");
		approveCodeMap.put("101106", "11_0003_0003");
		approveCodeMap.put("101107", "11_0003_0006");

	}
}


以上是我写的一个方式,总觉得别扭,大家一块看看有什么更好的方法。

2010年6月更新:
这个东西看着确实不是东西,有静态初始化块了,没必要有getter了,实在是。。。
public class ApproveMap {

	public static final Map<String, String> approveCodeMap = new HashMap<String, String>();
	static {
		/**
		 *
		 */
		approveCodeMap.put("101102", "11_0003_0001");
		approveCodeMap.put("101103", "11_0003_0004");
		approveCodeMap.put("101104", "11_0003_0005");
		approveCodeMap.put("101105", "11_0003_0002");
		approveCodeMap.put("101106", "11_0003_0003");
		approveCodeMap.put("101107", "11_0003_0006");

	}
}
分享到:
评论
60 楼 xcly 2009-03-18  
结贴,不要复杂化
59 楼 andot 2009-03-13  
insiku 写道
andot 写道
找不到服务器 写道
初始化需要同步这个思想是正确的,但是不需要我们去显式的干这个事情,JVM会"自动加锁"

比如,线程T1调用某个类A,若A没有被加载,那么A会被JVM加载,如果在加载的过程中有其他线程T2也要调用A,T2那么只能等待,而不是再去加载。也许在加载过程中T1的时间片到期,但是T2还是会继续阻塞,直到A被加载完成。所以这个过程就好像JVM自动加锁了一样。。。。。。。。。。。


嗯,我那个测试程序就是来说明这一点的。我愿意为 JVM 不会“自动加锁”,可是测试结果表明 JVM 会“自动加锁”,所以表明我以前的想法是错的。


锁只是为了多线程的数据同步而已
如果加载类 只是一个线程的话 那又哪来锁的概念???
你这样为某种现象强加自圆其说的解释 对后来人是会造成误解的


我的例子里有7个并发线程在访问同一个数据,当然是涉及到多线程的数据同步了,怎么会只有一个线程呢。
58 楼 insiku 2009-03-13  
andot 写道
找不到服务器 写道
初始化需要同步这个思想是正确的,但是不需要我们去显式的干这个事情,JVM会"自动加锁"

比如,线程T1调用某个类A,若A没有被加载,那么A会被JVM加载,如果在加载的过程中有其他线程T2也要调用A,T2那么只能等待,而不是再去加载。也许在加载过程中T1的时间片到期,但是T2还是会继续阻塞,直到A被加载完成。所以这个过程就好像JVM自动加锁了一样。。。。。。。。。。。


嗯,我那个测试程序就是来说明这一点的。我愿意为 JVM 不会“自动加锁”,可是测试结果表明 JVM 会“自动加锁”,所以表明我以前的想法是错的。


锁只是为了多线程的数据同步而已
如果加载类 只是一个线程的话 那又哪来锁的概念???
你这样为某种现象强加自圆其说的解释 对后来人是会造成误解的
57 楼 huangdan817 2009-03-04  
使用静态块
56 楼 mhz_1986 2009-03-03  
静态初始化不错,简洁!
55 楼 icewubin 2009-03-01  
pipilu 写道
我指的是yyjn12的回复。我在我的回复中引用了yyjn12的发言。但你似乎理解成了我在针对楼主的代码来说的。
这个没必要在追究了。

嗯,仔细看了下确实是我搞错了。
54 楼 pipilu 2009-03-01  
icewubin 写道
pipilu 写道
他加上双重检查就是为了在第二次访问时不必再执行同步那一段了,因为原有的代码在判断if (approveCodeMap.isEmpty())时需要获得锁 ,但实际上,第一次访问之后,已经setApprove()了,以后的访问还需要获得锁,这种开销就很不必要了。这是他加双重检查的目的,所以无所谓“影响性能”。

但是,在一个线程持有锁执行setApprove()时,另一个线程approveCodeMap.isEmpty()的判断会为false,那返回的是没有初始化完全的approveCodeMap。这不是开发者期望的。
另外,不提倡双重检查的原因主要是“内存的无序写入”的问题。
http://www.ibm.com/developerworks/cn/java/j-dcl.html

这个问题在之前javaeye的一个帖子的回复中被讨论过。

你搞错了,双重检查的例子中不是这样的。他这个例子连双重检查都没有达到。

先不考虑“双重检查”是否能实现,“双重检查”例子的初衷是避免每次都执行影响性能的同步代码,所以“双重检查”例子的同步代码是在第二次检查中的,不是第一次,所以你看到的连双重检查都不是,而是一种很简单的一般同步代码,性能很差的一种(因为每次都要同步)。

双重检查的例子如下:
private Resource resource;

public Resource getResource() {
  if (resource == null) { 
    synchronized(this) { 
      if (resource==null) {
        resource = new Resource();  
      }   
    }  
  }
  return resource;
}

pipilu 写道
另,关于那个“自动加锁”的比喻,我不理解的地方在于:难道存在这种情况——类加载的过程会被并发访问(并发加载)?

这个很好理解,表面上同步的实现,并不仅仅是加锁才能实现的,仅仅是一种效果。
这牵涉到JVM的类加载机制,仅仅是一个保证在调用前,类加载能够确保加载完毕,仅此而已,至于用了什么技术来确保接加载完毕,这个方法多了去了,并一定只有加锁才能实现吧。


我指的是yyjn12的回复。我在我的回复中引用了yyjn12的发言。但你似乎理解成了我在针对楼主的代码来说的。
这个没必要在追究了。

第二个问题,我的意思是,如果类加载的机制决定了根本不可能出现并发加载的情况,那就不必说什么“自动加锁”的话。比如,java中给一个整型的变量赋值时,是线程安全的,我们总不能说成“在给整型赋值时,jvm给它自动加了锁”吧?会让人犯晕的。这个我去了解一下类加载机制吧。
53 楼 icewubin 2009-03-01  
pipilu 写道
他加上双重检查就是为了在第二次访问时不必再执行同步那一段了,因为原有的代码在判断if (approveCodeMap.isEmpty())时需要获得锁 ,但实际上,第一次访问之后,已经setApprove()了,以后的访问还需要获得锁,这种开销就很不必要了。这是他加双重检查的目的,所以无所谓“影响性能”。

但是,在一个线程持有锁执行setApprove()时,另一个线程approveCodeMap.isEmpty()的判断会为false,那返回的是没有初始化完全的approveCodeMap。这不是开发者期望的。
另外,不提倡双重检查的原因主要是“内存的无序写入”的问题。
http://www.ibm.com/developerworks/cn/java/j-dcl.html

这个问题在之前javaeye的一个帖子的回复中被讨论过。

你搞错了,双重检查的例子中不是这样的。他这个例子连双重检查都没有达到。

先不考虑“双重检查”是否能实现,“双重检查”例子的初衷是避免每次都执行影响性能的同步代码,所以“双重检查”例子的同步代码是在第二次检查中的,不是第一次,所以你看到的连双重检查都不是,而是一种很简单的一般同步代码,性能很差的一种(因为每次都要同步)。

双重检查的例子如下:
private Resource resource;

public Resource getResource() {
  if (resource == null) { 
    synchronized(this) { 
      if (resource==null) {
        resource = new Resource();  
      }   
    }  
  }
  return resource;
}

pipilu 写道
另,关于那个“自动加锁”的比喻,我不理解的地方在于:难道存在这种情况——类加载的过程会被并发访问(并发加载)?

这个很好理解,表面上同步的实现,并不仅仅是加锁才能实现的,仅仅是一种效果。
这牵涉到JVM的类加载机制,仅仅是一个保证在调用前,类加载能够确保加载完毕,仅此而已,至于用了什么技术来确保接加载完毕,这个方法多了去了,并一定只有加锁才能实现吧。
52 楼 pipilu 2009-03-01  
icewubin 写道
pipilu 写道
yyjn12 写道

# synchronized (approveCodeMap) { 
#             if (approveCodeMap.isEmpty()) 
#                 setApprove(); 
#         } 
的外边,再加一层  if(approveCodeMap.isEmpty())

双重检查锁,效率会比这个高很多。
这个不成了,每次取这个map都要排队了吗


已经有很多文章说明双重检查锁是行不通的,为什么还有人在推崇呢?

这里一旦加了同步synchronized,就不再是你说的那个“无效双重检查锁”了。

但是我照样不推崇,因为同步锁在极端情况下会严重影响性能的。


他加上双重检查就是为了在第二次访问时不必再执行同步那一段了,因为原有的代码在判断if (approveCodeMap.isEmpty())时需要获得锁 ,但实际上,第一次访问之后,已经setApprove()了,以后的访问还需要获得锁,这种开销就很不必要了。这是他加双重检查的目的,所以无所谓“影响性能”。

但是,在一个线程持有锁执行setApprove()时,另一个线程approveCodeMap.isEmpty()的判断会为false,那返回的是没有初始化完全的approveCodeMap。这不是开发者期望的。
另外,不提倡双重检查的原因主要是“内存的无序写入”的问题。
http://www.ibm.com/developerworks/cn/java/j-dcl.html

这个问题在之前javaeye的一个帖子的回复中被讨论过。

另,关于那个“自动加锁”的比喻,我不理解的地方在于:难道存在这种情况——类加载的过程会被并发访问(并发加载)?
51 楼 icewubin 2009-02-28  
pipilu 写道
static区块应该是jvm负责运行的,我想不出有什么可能性会使它被“并发访问”??我们怎么去并发访问它?
莫非类加载器会启动多个线程去加载这个类?  想不明白。
关于“自动给static加上锁”,我用javap看了一下jvm指令,没看到“monitorenter”和“monitorexit”指令,没理由证明static被自动加上锁了。

他们说的“自动上锁”只是个比喻。
50 楼 icewubin 2009-02-28  
pipilu 写道
yyjn12 写道

# synchronized (approveCodeMap) { 
#             if (approveCodeMap.isEmpty()) 
#                 setApprove(); 
#         } 
的外边,再加一层  if(approveCodeMap.isEmpty())

双重检查锁,效率会比这个高很多。
这个不成了,每次取这个map都要排队了吗


已经有很多文章说明双重检查锁是行不通的,为什么还有人在推崇呢?

这里一旦加了同步synchronized,就不再是你说的那个“无效双重检查锁”了。

但是我照样不推崇,因为同步锁在极端情况下会严重影响性能的。
49 楼 pipilu 2009-02-28  
static区块应该是jvm负责运行的,我想不出有什么可能性会使它被“并发访问”??我们怎么去并发访问它?
莫非类加载器会启动多个线程去加载这个类?  想不明白。
关于“自动给static加上锁”,我用javap看了一下jvm指令,没看到“monitorenter”和“monitorexit”指令,没理由证明static被自动加上锁了。
48 楼 xiaoZ5919 2009-02-28  
static语句块  就可以了
47 楼 pipilu 2009-02-28  
yyjn12 写道

# synchronized (approveCodeMap) { 
#             if (approveCodeMap.isEmpty()) 
#                 setApprove(); 
#         } 
的外边,再加一层  if(approveCodeMap.isEmpty())

双重检查锁,效率会比这个高很多。
这个不成了,每次取这个map都要排队了吗


已经有很多文章说明双重检查锁是行不通的,为什么还有人在推崇呢?
46 楼 icewubin 2009-02-27  
zhajie 写道
public class testDao  {
	private testDao  () {
	}

	private static testDao  config = new testDao  ();
	private Map<String, Map<String,Config>> configs = null;

	public static testDao  getInstance() {
		return config;
	}
	public  void inCache() {
		if(configs==null)
			configs = new ConcurrentHashMap<String, Map<String,Config>>();
	
}
}



这样不就ok了!

你这个就是Java的饿汉式单例模式。
45 楼 zhajie 2009-02-27  
public class testDao  {
	private testDao  () {
	}

	private static testDao  config = new testDao  ();
	private Map<String, Map<String,Config>> configs = null;

	public static testDao  getInstance() {
		return config;
	}
	public  void inCache() {
		if(configs==null)
			configs = new ConcurrentHashMap<String, Map<String,Config>>();
	
}
}



这样不就ok了!
44 楼 icewubin 2009-02-27  
blurm 写道

这个spring的初始化功能是指的哪一部分呢?愿闻其详~~

你看第三页最后一段,我写了。

这里给个简单例子。
icewubin 写道
<bean id="driver" class="com.javaeye.Driver" init-method="init"/>


43 楼 jwinder 2009-02-27  
推崇
satic {
......
}
42 楼 blurm 2009-02-27  
icewubin 写道
andot 写道
能不能说一下,Class.forName()在何时调用,就是说写在代码的哪个位置,对这个比较感兴趣,以前没接触过这个,所以一直都是在 static 块中加锁来保证线程不会冲突的,因为原来没加锁时,确实遇到过线程冲突的问题。就是在 Web 服务器上部署是遇到的。

这个。。。最好把你碰到的情况详细说一下(例如static代码块如果有依赖于访问数据库的操作,那是最好换一种思路了)。

一般来说web.xml中可以指定初始化的顺序(例子我现在没有,明天到单位才),比如有一个类叫做com.javaeye.Driver,那只要最优先触发一下Class.forName("com.javaeye.Driver")就可以了。

说管说,真要做的话,因为我使用Spring,使用Spring的初始化功能来做的(要例子的话明天给)。static只能做做简单的初始化,例如map的初始化等等,复杂的初始化最好不要用static代码块。


这个spring的初始化功能是指的哪一部分呢?愿闻其详~~
41 楼 jieyuan_cg 2009-02-26  
andot 写道
经过实际测试发现,我原来认为的是错的。确实静态 static 块自己会加锁,不需要再手工用同步块加锁了。下面是测试代码:

package teststatic;

import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class MyStatic {
    private static HashMap<String, String> map = new HashMap<String, String>();
    static {
        try {
            System.out.println("start init");
            map.put("101101", "11_0003_0007");
            Thread.sleep(1000);
            map.put("101102", "11_0003_0001");
            Thread.sleep(1000);
            map.put("101103", "11_0003_0004");
            Thread.sleep(1000);
            map.put("101104", "11_0003_0005");
            Thread.sleep(1000);
            map.put("101105", "11_0003_0002");
            Thread.sleep(1000);
            map.put("101106", "11_0003_0003");
            Thread.sleep(1000);
            map.put("101107", "11_0003_0006");
            System.out.println("end init");
        }
        catch (InterruptedException ex) {
            Logger.getLogger(MyStatic.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public static String getData(String key) {
        return map.get(key);
    }
}

package teststatic;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 1; i < 8; i++) {
            final int n = i;
            Runnable r = new Runnable() {
                public void run() {
                    System.out.println("start call" + n);
                    System.out.println(MyStatic.getData("10110" + n));
                    System.out.println("end call" + n);
                }
            };
            new Thread(r).start();
            Thread.sleep(50);
        }
    }
}

运行结果:
start call1
start init
start call2
start call3
start call4
start call5
start call6
start call7
end init
11_0003_0006
end call7
11_0003_0007
end call1
11_0003_0002
11_0003_0005
end call4
11_0003_0001
end call2
11_0003_0003
11_0003_0004
end call3
end call5
end call6

嗯,这个测试代码已经很明显地显示了static块加载的时间了~呵呵~

相关推荐

    第5天static静态关键字和单例模式.pptx

    static静态关键字和单例模式

    单例模式中声明静态自己类型的指针编译显示未定义处理

    主要解决在单例模式下类中声明静态指针存储单例对象,在.cpp文件中编译时显示未定义的解决办法: 参考博客: http://bbs.csdn.net/topics/10439749

    单例模式的多种实现.docx

    单例模式的七种实现方法以及分析,可以作文大作业提交 ...3.6使用静态内部类实现单例模式 12 3.7使用枚举类实现单例模式 13 4.单例模式怎么用才合理? 14 4.1测试 14 4.2无边界 15 5.设计模式学习总结: 15

    Java静态内部类实现单例过程

    主要介绍了Java静态内部类实现单例过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    QT静态单例管理信号和槽

    QT 单例

    joomla里面的单例模式和纯静态类

    在Joomla!涉及到了很多的单例模式,比如JFactory,JURI等等。 对于一个请求中需要一个对象实例的,joomla大多采用了单例模式,可以避免重复实例化带来的资源浪费和性能损耗。

    .NET :静态类的理解

    静态类成员可用于分离独立于任何对象标识的数据和行为:无论对象发生什么更改 这些数据和函数都不会随之变化 当类中没有依赖对象标识的数据或行为时 就可以使用静态类 "&gt;静态类是一种声明为 static 类型的 且仅包含...

    php单例模式实例

    如果说php是一门面向对象编程的话,那么设计模式就是它的灵魂,其中单例模式就是设计模式的重中之重了,分享鄙人一直用的单例模式。

    Java中的单例模式与静态类

    单例模式与静态类(一个类,所有方法为静态方法)是另一个非常有趣的问题,在《Java中有关单例模式的面试问题》博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例如,两者可以直接...

    Java单例模式实现静态内部类方法示例

    主要介绍了Java单例模式实现静态内部类方法示例,涉及构造函数私有化等相关内容,需要的朋友可以了解下。

    java 设计模式 单例模式

    //单例模式,始终只产生一个对象 /*public class SingleTest { public static void main(String[] args) { SingleTon singleTon1=SingleTon.getInstance(); SingleTon singleTon2=SingleTon.getInstance(); ...

    这可能是最全的单例模式了

    静态内部类实现单例模式5. 饿汉实现单例模式6. 饿汉变种实现单例模式7. 枚举实现单例模式static修饰下是怎么做到线程安全的?完全不使用synchronized实现单例模式1. CAS(AtomicReference)实现单例模式2. ...

    java使用静态关键字实现单例模式

    主要为大家详细介绍了java使用静态关键字实现单例模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    静态内部类

    静态内部类

    php中静态类与静态变量用法类.zip

    直接调用类方法量:class::attribute/function,无论是静态/非静态都可以,静态static:声明类成员或方法为 static,就可以不实例化类而直接访问,不能通过一个对象来访问其中的静态成员(静态方法除外),静态成员属于类,不...

    JS定义静态类

    JS定义静态类,可以看看,应该很容易看懂

    单例模式 工厂模式DEMO

    而静态类是把数据封装在类里,操作类。单例模式中,数据只有在对象存在的时候才能操作;而静态类中,数据可以直接通过静态类点出来操作。 2、工厂模式 结论:工厂模式体现在接口作为返回类型的运用中,实际上返回的...

    线程相关的单例模式

    本工程实现了一种利用静态工厂和单例模式两种模式思路设计的线程相关的单例模式。

    面向对象模拟静态类和构造对象

    面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象面向对象模拟静态类和构造对象

Global site tag (gtag.js) - Google Analytics