`
5452
  • 浏览: 28862 次
  • 性别: 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块加载的时间了~呵呵~

相关推荐

    QT静态单例管理信号和槽

    QT静态单例管理信号和槽是Qt框架中一种常见的设计模式,用于确保应用程序中只有一个特定类的实例。在Qt编程中,单例模式通常用于管理全局资源,如数据库连接、配置文件读取或系统设置。这里我们将深入探讨如何在Qt中...

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

    在Java中,有多种实现单例的方式,包括饿汉式、懒汉式、双重检查锁定(DCL)、静态内部类和枚举。这里我们将重点讨论静态内部类和枚举实现单例的过程。 首先,让我们来看看静态内部类实现单例的方式: ```java ...

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

    单例模式是软件设计模式中的一种,用于控制类的实例化过程,确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统中需要频繁创建和销毁的对象,或者需要共享资源的情况下非常有用。然而,实现单例模式时,...

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

    在Java中,单例模式可以通过多种方式实现,包括懒汉式、饿汉式、双重检查锁定和静态内部类方法等。今天,我们主要介绍了Java单例模式实现静态内部类方法示例,涉及构造函数私有化等相关内容。 单例模式的定义 单例...

    Java 单例模式 工具类

    通过`SingletonFactory`工具类,我们不仅可以方便地使用各种单例模式,还可以对自定义的单例进行统一管理和访问,提高了代码的可维护性和灵活性。在实际开发中,应根据项目需求选择适合的单例实现方式,以保证代码的...

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

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

    php单例模式实例

    单例模式的核心思想是限制类的实例化过程,确保在程序运行期间,类的实例只有一个。通过控制类的构造函数,使其不能被外部直接实例化,而是通过一个静态方法来获取唯一的实例。这样,无论何时何地,只要调用这个静态...

    Java中的静态变量静态方法静态块与静态类.docx

    Java 中的静态变量、静态方法、静态块和静态类 Java 中的静态变量、静态方法、静态块和静态类是 Java 编程语言的四个重要概念,它们之间存在着紧密的关系。下面将对这四个概念进行详细的介绍。 一、静态变量...

    设计模式之单例模式(结合工厂模式)

    此外,单例模式还有几种变体,比如静态内部类单例和枚举单例。静态内部类单例利用Java类加载机制保证了线程安全,而枚举单例则是Java中实现单例的最佳方式,因为它天然支持序列化且防止反射攻击。 在代码实现上,...

    java中的单例模式

    静态内部类单例利用JVM保证了类加载的线程安全性,而枚举单例则是一种既简洁又线程安全的实现方式,也是官方推荐的单例实现方式。 ```java public enum Singleton { INSTANCE; // ... } ``` 在使用单例模式时,...

    Java中的单例模式与静态类

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

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

    在上面的代码中,我们定义了一个 SingletonMode 类,它有一个私有的构造方法和一个公有的静态方法 getInstance()。getInstance() 方法用于获取 SingletonMode 的实例,如果实例不存在,则创建一个新的实例。 测试...

    java单例设计模式 4中实现方式,重点介绍了静态内部类的实现方式

    静态内部类单例模式利用了Java语言中静态内部类的特性来实现单例模式。具体来说它将单例对象的创建延迟到静态内部类被加载时才执行,从而避免了多线程环境下的同步问题。下面是一个简单的示例代码: ```java public...

    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. ...

    singleton_crash:演示由多个动态库链接的静态库中的单例导致的崩溃

    然而,如果不正确地处理单例,特别是在涉及动态库和静态库的情况下,可能会导致各种问题,甚至引发程序崩溃。标题和描述所提到的"singleton_crash"就是这种情况的一个实例,它探讨了当静态库被多个动态库链接时,...

    静态内部类

    根据内部类是否声明为static,它们被分为非静态内部类(也称为成员内部类)和静态内部类。 #### 二、静态内部类的特点 静态内部类具有以下特点: 1. **独立性**:静态内部类与外部类之间没有依赖关系,即使外部类...

    c++单例模式线程日志类

    在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、精确的时间戳以及可变长参数和标准格式化输出的功能。 首先,让我们深入了解单例模式。单例模式的主要...

    Oracle jdbc 单例 工具类

    - 静态内部类单例:利用类加载机制保证线程安全,避免了同步开销。 4. **工具类设计**: - `DB.java`:可能包含静态方法,如`getConnection()`,返回数据库连接。使用单例模式确保全局只存在一个`Connection`实例...

Global site tag (gtag.js) - Google Analytics