java设计模式 单例模式_Java设计模式-单例模式 (Singleton Pattern)

在一些程序设计中,希望对象只有一个实例,这时候就可以使用单例模式。

单例模式的实现,在语法上 用一个私有的构造方法来保护类不能在外部被 new 出来,然后提供一个静态方法返回唯一的实例即可。

应用场景,例如:系统配置,整个系统有一个配置对象即可,如果有配置修改,通知这个唯一的对象就好了,每次读取配置只需从这个唯一的对象中获取。

下面是一些常见的写法,以及优缺点:

实现方式一

package cn.devdoc.dp.creational.singleton;

/**

*

* 最简单的单例模式,在多线程的情况下依然能保持单例。

*

*

* @author CK

*

*/

public class Singleton1 {

private final static Singleton1 instance = new Singleton1();

static {

// 在这里初始化 instance 其实都一样,都是在类初始化即实例化instance。

}

private Singleton1() {

}

public static Singleton1 getInstance() {

return instance;

}

}

这是最简单的单例模式,在多线程的情况下依然能保持单例。

这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,没有达到lazy loading的效果。

实现方式二

package cn.devdoc.dp.creational.singleton;

/**

*

* @author CK

*

*/

public class Singleton2 {

private static Singleton2 instance;

private Singleton2() {

}

public static Singleton2 getInstance() {

if (instance == null) {

// 在多线程的时候这里会出问题,导致的后果就是创建了多个实例

// 为测试效果,假设这里需要5秒才能完成创建实例

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

instance = new Singleton2();

}

return instance;

}

}

在调用getInstance方法时才实例化对象,但多线程环境下会创建出多个对象。

实现方式三

package cn.devdoc.dp.creational.singleton;

/**

*

* 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

*

*

* @author CK

*

*/

public class Singleton3 {

private static Singleton3 instance = null;

private Singleton3() {

}

public static synchronized Singleton3 getInstance() {

if (instance == null) {

// 为测试效果,假设这里需要5秒才能完成创建实例

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

instance = new Singleton3();

}

return instance;

}

}

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,除了第一次,其它情况都不需要同步。

实现方式四

package cn.devdoc.dp.creational.singleton;

/**

*

* 双检锁,解决同步锁的性能问题,只有还没实例化的时候才会锁

*

*

* @author CK

*

*/

public class Singleton4 {

private volatile static Singleton4 instance;

private Singleton4() {

}

public static Singleton4 getInstance() {

if (instance == null) {

// 只有instance还没被实例化的时候才会到这里,但有可能多个线程都执行到这了。

synchronized (Singleton4.class) {

// 进入到if中的线程有多个,前面的线程可能已经实例化了instance,所以需要再次判断。

if (instance == null) {

instance = new Singleton4();

}

}

}

return instance;

}

}

双检锁,解决同步锁的性能问题,只有还没实例化的时候才会锁

实现方式五

package cn.devdoc.dp.creational.singleton;

/**

*

* 基于静态内部类实现的单例模式

*

*

* @author CK

*

*/

public class Singleton5 {

private static class SingletonHolder {

private static final Singleton5 INSTANCE = new Singleton5();

}

private Singleton5() {

}

public static Singleton5 getInstance() {

return SingletonHolder.INSTANCE;

}

}

通过内部类实现的,其它还有通过枚举实现的。

测试

package cn.devdoc.dp.creational.singleton;

import org.junit.Assert;

import org.junit.Test;

public class SingletonTest {

@Test

public void test0() {

SingletonTest dp1 = new SingletonTest();

SingletonTest dp2 = new SingletonTest();

Assert.assertFalse(dp1.equals(dp2));

Assert.assertFalse(dp1.hashCode() == dp2.hashCode());

}

@Test

public void test1() {

Singleton1 ins1 = Singleton1.getInstance();

Singleton1 ins2 = Singleton1.getInstance();

Assert.assertTrue(ins1.equals(ins2));

Assert.assertTrue(ins1.hashCode() == ins2.hashCode());

System.out.println(ins1);

System.out.println(ins1.hashCode());

// 模拟多线程的场景

Runnable r = new Runnable() {

@Override

public void run() {

Singleton1 single = Singleton1.getInstance();

System.out.println(single.hashCode());

}

};

for (int i = 0; i < 10; i++) {

new Thread(r).start();

}

}

@Test

public void test2() {

// 模拟多线程的场景,多运行几次会发现有不一样的hashCode

Runnable r = new Runnable() {

@Override

public void run() {

Singleton2 single = Singleton2.getInstance();

System.out.println(single.hashCode());

}

};

for (int i = 0; i < 10; i++) {

new Thread(r).start();

}

}

@Test

public void test3() {

// 模拟多线程的场景,无论怎么运行hashCode都是一样的

Runnable r = new Runnable() {

@Override

public void run() {

Singleton3 single = Singleton3.getInstance();

System.out.println(single.hashCode());

}

};

for (int i = 0; i < 10; i++) {

new Thread(r).start();

}

}

@Test

public void test4() {

// 模拟多线程的场景,无论怎么运行hashCode都是一样的

Runnable r = new Runnable() {

@Override

public void run() {

Singleton4 single = Singleton4.getInstance();

System.out.println(single.hashCode());

}

};

for (int i = 0; i < 10; i++) {

new Thread(r).start();

}

}

@Test

public void test5() {

Runnable r = new Runnable() {

@Override

public void run() {

Singleton5 single = Singleton5.getInstance();

System.out.println(single.hashCode());

}

};

for (int i = 0; i < 10; i++) {

new Thread(r).start();

}

}

}

测试结果

test0:测试出普通类new出来的2个对象 地址和hashCode都不一样

test1:模拟线程测试,hashCode是一样的

test2:模拟线程测试,会有hashCode不一样的情况,这是多线程造成的。

test3:模拟线程测试,无论怎么测试,hashCode都一样

test4:模拟线程测试,无论怎么测试,hashCode都一样

test5:模拟线程测试,无论怎么测试,hashCode都一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值