`
5452
  • 浏览: 29009 次
  • 性别: 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中,单例模式可以通过多种方式实现,包括懒汉式、饿汉式、双重检查锁定和静态内部类方法等。今天,我们主要介绍了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中有关单例模式的面试问题》博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例如,两者可以直接...

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

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

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

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

    使用单例模式实现计数器

    在C#中,我们可以利用单例模式来创建一个计数器类,以确保在整个应用程序的生命周期内,计数器只存在一个实例,并且能够被多个对象安全地共享和访问。 首先,我们需要理解单例模式的基本原理。单例模式的关键在于...

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

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

    静态内部类

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

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

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

    Oracle jdbc 单例 工具类

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

    Sqlite3 C++ 简单单例数据库操作类封装

    单例模式的实现通常包括一个私有的静态成员变量来保存类的唯一实例,以及一个公共的静态成员函数(如`getInstance()`)来获取这个实例。为了防止多次实例化,构造函数通常设为私有,并且在类外部不能被直接调用。 ...

Global site tag (gtag.js) - Google Analytics