单例模式
WHAT
单例是全局只有一个实例的代码设计方式
WHY
应用场景:
只需要一个实例,比如各种的工厂类,全局只需要一个就可以了,所以不需要让它被new出多个.
如何实现单例呢?
HOW
实现单例的方式1:饿汉式(最常用的方式,并且是线程安全的)
/**
* 饿汉式
* 类加载到内存后,就实例化一个单例,JVM保证线程安全
* 简单实用,推荐使用!
* 唯一缺点:不管用到与否,类装载时就完成实例化
* Class.forName("")
* (话说你不用的,你装载它干啥)
*/
public class HugoFactory {
// 0,创建需要的实例对象
private static final HugoFactory hugoFactoryInstance = new HugoFactory();
// 1,构造方法私有:为了不让别人通过new的方式的创建对象
private HugoFactory() {
}
// 2,创建对象并对外提供获取对象的方法
public static HugoFactory getInstance() {
return hugoFactoryInstance;
}
// 测试
public static void main(String[] args) {
HugoFactory hugoFactory1 = HugoFactory.getInstance();
HugoFactory hugoFactory2 = HugoFactory.getInstance();
System.out.println(hugoFactory1 == hugoFactory2);
}
}
/**
* 跟01是一个意思
*/
public class Mgr02 {
private static final Mgr02 INSTANCE;
static {
INSTANCE = new Mgr02();
}
private Mgr02() {};
public static Mgr02 getInstance() {
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
Mgr02 m1 = Mgr02.getInstance();
Mgr02 m2 = Mgr02.getInstance();
System.out.println(m1 == m2);
}
}
实现单例的方式2:懒汉式(有线程安全问题)
package com.mashibing.dp.singleton.demo_mine;
/**
* 懒汉式:获取对象实例的时候加载
* 虽然达到了按需初始化的目的,但却带来线程不安全的问题
*/
public class HugoFactory02 {
private static HugoFactory02 INSTANCE;
// 1,构造方法私有:为了不让别人通过new的方式的创建对象
private HugoFactory02() {
}
// 2,创建对象并对外提供获取对象的方法
public static HugoFactory02 getInstance() {
// 如果当前没有实例则创建实例并返回
if (INSTANCE == null) {
// 睡眠一会
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new HugoFactory02();
return INSTANCE;
}
return INSTANCE;
}
// 测试
public static void main(String[] args) {
// 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 获取实例
HugoFactory02 instance = HugoFactory02.getInstance();
System.out.println("instance = " + instance);
}
}).start();
}
}
/** 测试结果:获取到了不同的实例对象,因为多条线程访问了共享资源
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@d438b49
解决方案:加锁
synchronized同步方法就行了
synchrnized代码块:注意块的范围,要包裹住共享数据
*/
}
实现单例的方式3:懒汉双判空
package com.mashibing.dp.singleton.demo_mine;
/**
* 懒汉式:获取对象实例的时候加载
* 虽然达到了按需初始化的目的,但却带来线程不安全的问题
* 双判空写法
*/
public class HugoFactory03 {
private static HugoFactory03 INSTANCE;
// 1,构造方法私有:为了不让别人通过new的方式的创建对象
private HugoFactory03() {}
// 2,创建对象并对外提供获取对象的方法,加synchronized使线程同步,解决安全问题
public static HugoFactory03 getInstance() {
// 如果当前没有实例则创建实例并返回
if (INSTANCE == null) {
synchronized (HugoFactory02.class) {
if (INSTANCE == null) {
// 睡眠一会
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new HugoFactory03();
return INSTANCE;
}
}
}
return INSTANCE;
}
// 测试
public static void main(String[] args) {
// 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 获取实例
HugoFactory03 instance = HugoFactory03.getInstance();
System.out.println("instance = " + instance);
}
}).start();
}
}
/**
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@260d0705
instance = com.mashibing.dp.singleton.demo_mine.HugoFactory02@d438b49
*/
}
实现单例的方式4:静态内部类方式
package com.mashibing.dp.singleton.demo_mine;
/**
* 静态内部类的方式:内部类负责创建实例对象
*/
public class HugoFactory04 {
private HugoFactory04() {
}
// 静态内部中创建实例,jvm进行管理,保证了线程的安全
private static class HugoFactoryHolder {
private static final HugoFactory04 INSTANCE = new HugoFactory04();
}
public static HugoFactory04 getInstance(){
return HugoFactoryHolder.INSTANCE;
}
// 测试
public static void main(String[] args) {
// 模拟线程安全问题,创建若干线程同时获取INSTANCE实例
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
// 获取实例
HugoFactory04 instance = HugoFactory04.getInstance();
System.out.println("instance = " + instance);
}
}).start();
}
}
}
实现单例的方式5:枚举方式
package com.mashibing.dp.singleton;
/**
* 不仅可以解决线程同步,还可以防止反序列化。
* 枚举类没有构造方法,所以不能被反序列化
*/
public enum HugoFactory05 {
INSTANCE;
public void m() {}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(HugoFactory05.INSTANCE.hashCode());
}).start();
}
}
}