1 线程不安全的实现方法
首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到。这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式。但是如果不需要保证线程安全,则这种方式还是不错的,因为所需要的开销比较小。下面是具体的实现代码:
public Class Singleton
{
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance()
{
if( instance == null)
instance = new Singleton ();
return instance;
}
}
我们说过这种实现方式不是thread-safe的,那么可以把上面的方法变成线程安全的吗?当然可以,在方法getInstance()上加上synchronized修饰符就可以实现方法的同步了。但是这样系统开销会很大。具体代码如下:
public Class Singleton
{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance()
{
if( instance == null)
instance = new Singleton ();
return instance;
}
}
每次有线程调用getInstance()方法,都需要同步判断。这显然不是最好的选择,下面将会陆续介绍几种thread-safe的方法。
2 两种lazy loaded thread-safe的单例模式实现方式
1) DCL (double checked locking 实现法)
double checked locking ,顾名思义,就是双检查法,检查实例INSTANCE是否为null或者已经实例化了。下面是具体的实现代码:
public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
这种方法也很好理解,我们可以看到有两次对instance是否为null的判断:如果第一次判断不为空,则直接返回实例就可以了;如果instance为空,则进入同步代码块再进行null值判断,再选择是否实例化。第一个null判断可以减少系统的开销。在实际项目中做过多线程开发的都应该知道DCL。
2) lazy initialization holder class 模式实现法
下面是这种方法的实现代码:
public class Singleton {
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
/**
* 私有化构造方法
*/
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
关于延迟初始化(lazy loaded)
“除非绝对必要,否则就不要延迟初始化”。延迟初始化是一把双刃剑,它降低了初始化类或者创建实例的开销,却增加了访问被延迟初始化的域的开销,考虑到延迟初始化的域最终需要初始化的开销以及域的访问开销,延迟初始化实际上降低了性能。
3 静态工厂实现法
因为单例是静态的final变量,当类第一次加载到内存中的时候就初始化了,其thread-safe性由JVM来负责保证。值得注意的是这个实现方式不是lazy-loadedd的。 具体实现代码如下:
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
4 枚举实现单例(Enum Singleton)
枚举单例(Enum Singleton)是实现单例模式的一种新方式,枚举这个特性是在Java5才出现的,在《Effective Java》一书中有介绍这个特性。下面是这种方法的具体实现代码:
public enum Singleton {
INSTANCE("hello") {
public void someMethod() {
// . . .
}
};
private String name;
private void PrintName(){System.out.println(name);}
protected abstract void someMethod();
}
你可以通过Singleton.INSTANCE来访问该单示例变量。默认枚举实例的创建是线程安全的,但是在枚举中的其他任何方法由程序员自己负责。如果你正在使用实例方法,那么你需要确保线程安全(如果它影响到其他对象的状态的话)。传统单例存在的另外一个问题是一旦你实现了序列化接口,那么它们不再保持单例了,但是枚举单例,JVM对序列化有保证。枚举实现单例的好处:有序列化和线程安全的保证,代码简单。
分享到:
相关推荐
#### EJ 第3条:用私有构造器或枚举类型强化Singleton属性 单例模式是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点。Kotlin通过`object`关键字进一步简化了单例模式的实现,使得创建...
- **副作用**:使用私有构造器的一个副作用是使得该类不能被继承。 **避免创建不必要的对象**: 1. **不可变对象**:对于不可变对象,可以通过缓存已创建的对象来避免重复创建。 2. **使用静态工厂方法**:如果一...
1. **避免反射攻击**:在私有构造器中添加条件判断,如`if (instance != null)`,这样即便反射尝试创建新实例也会被阻止。 2. **避免反序列化攻击**:为单例类实现`readResolve()`方法,确保即使反序列化也不会创建...
Java中的枚举类型是线程安全的,并且只会装载一次,设计者充分考虑到了线程安全问题,因此使用枚举实现单例模式是一种简洁而且高效的解决方案。 6. 容器式单例(Singleton Holder) 通过一个私有的静态内部类...
- 构造器私有化,防止其他类通过new关键字创建实例。 - 创建并保存该类的唯一实例,通常使用静态变量存储。 - 提供一个公共的静态方法或属性,以便外部获取这个唯一的实例。 常见的单例实现方式有以下几种: - **...
在Java中,`switch`语句可以作用于`byte`、`short`、`char`、`int`类型,也可以作用于枚举类型和字符串(从Java 7开始)。 #### 第三十二题:Singleton模式的实现 Singleton模式确保一个类只有一个实例,并提供一...
1. **私有构造器(Private Constructor)**:这是最常用的控制对象创建的方式,特别是用于实现单例模式。通过将类的构造器声明为私有,外部类无法直接实例化它。例如: ```java public class Singleton { private ...
而“singleton method.txt”很可能是讲解或展示了不同类型的单例实现方式,包括传统的单例模式和基于枚举的单例实现。 通过深入学习和实践这三个主题,你将能更好地理解和掌握Java的核心特性,以及在实际项目中如何...
1. **私有构造器**: 单例类的构造器通常被声明为`private`,这样其他类无法通过`new`关键字直接创建该类的实例。这是防止外部代码实例化单例对象的第一道防线。 2. **静态内部类**: 在上述例子中,单例对象`...
3. **系统必须能够访问该实例**:通常情况下,单实例模式会提供一个静态方法或者属性,以便外部代码能够获取到这个唯一的实例。 #### 单实例模式的应用场景 在软件开发中,单实例模式有着广泛的应用,比如打印机...
- **枚举方式**:利用枚举类型天然的线程安全性和唯一性实现单例模式,简洁高效。 #### 五、单例模式的适用场景 单例模式适用于以下情况: - 系统只需要一个实例对象时。 - 单例对象需要被共享,并且所有的实例...
32. **Singleton模式**:确保一个类只有一个实例,通常通过私有构造器和静态工厂方法实现。 以上是对Java面试中常见问题的详细解答,涵盖了语言基础、集合、并发、异常处理、设计模式等多个方面。掌握这些知识点...
•总之,第一步先找局部变量,第二步,内部类的属性,第三步。外部类的属性。 本文原创作者:pipi-changing 本文原创出处:http://www.cnblogs.com/pipi-changing/ 静态内部类 •如果用...
即当前类所在路径及其引用的第三方类库的路径,如第四节中的问题6所述)下的类库 加载到内存中。 开发者可以直接使用系统类加载器。 5. 设计模式:(框架中使用,是程序设计的高级思想) 1. 单例模式...
第三十,switch不能作用于byte,但Java 7开始可以作用于char和枚举类型。Java 14引入了switch表达式,可以作用于String。 第三十一,编写Singleton模式的代码,一种常见方式是饿汉式(静态初始化)或懒汉式(双检锁...
但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地...
例如,对于饿汉式单例模式,可以通过反射强制调用私有构造器或通过序列化-反序列化来创建新实例。为解决这些问题,可以采用枚举类型实现单例模式,或者在懒汉式的基础上进行优化,比如在类中加入防止反射创建实例的...
23. 构造器不能被 override,因为构造器具有与类相同的名称,且不能有返回类型。 24. 不能直接继承 String 类,因为String是final的。 25. 当一个线程进入synchronized方法后,其他线程无法进入该对象的其他...