Java设计模式浅析
第一章 简单工厂模式
第二章 工厂方法模式
第三章 抽象工厂模式
第四章 单例模式
文章目录
···
单例模式概述
单例模式(Singleton Pattern)是指确保一个类在任何情况下都
绝对只有一个实例,并提供一个全局访问点。
单例模式是创建性模式。单例模式在开发中应用非常广泛,
例如,Spring 框架中的 ApplicationContext、数据库的连接池等。
单例模式有如下实现方式:
● 饿汉式
● 懒汉式
● 注册式
饿汉单例模式
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题。
Spring 中 IOC 容器 ApplicationContext 本身就是典型的饿汉式单例模式。
1、示例代码
public class HungrySingleton {
// 1.私有构造
private HungrySingleton (){}
// 2.在类的内部创建自行实例
private static final HungrySingleton instance = new HungrySingleton();
// 3.提供获取唯一实例的方法(全局访问点)
public static HungrySingleton getInstance(){
return instance;
}
public void leaveBuilding1(){
System.out.println("void:Hello1 World1");
}
public static void leaveBuilding(){
System.out.println("static void: Hello World");
}
}
2、测试代码
public class SingletonTest {
public static void main(String[] args) {
HungrySingleton a = HungrySingleton.getInstance();
//getInstance 获取实例
a.leaveBuilding1();
//静态类的静态方法直接调用
HungrySingleton.leaveBuilding();
System.out.println(HungrySingleton.getInstance());
System.out.println(HungrySingleton.getInstance());
System.out.println(HungryStaticSingleton.getInstance());
System.out.println(HungryStaticSingleton.getInstance());
}
}
懒汉单例模式(线程不安全)
构造器私有!!!
暴露getInstance()方法!!!
在第一次被调用的时候进行初始化
1、示例代码
public class LazySimpleSingleton {
private LazySimpleSingleton() {
}
//静态实例 默认置为NULL
private static LazySimpleSingleton lazy = null;
public static LazySimpleSingleton getInstance() {
if (lazy == null) {
lazy = new LazySimpleSingleton();
}
return lazy;
}
}
2、测试代码
public class LazySingletonTest {
public static void main(String[] args) {
//创建线程 调用run()方法
//线程不安全的
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t2.start();
System.out.println("线程不安全");
t1.start();
}
}
懒汉单例模式(synchronized)
1、示例代码
ExectorThread
public class ExectorThread implements Runnable{
@Override
public void run() {
//run方法实现 runnable 接口
LazySimpleSingleton singleton = LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + singleton);
}
}
ExectorThread
public class ExectorThread implements Runnable {
@Override
public void run() {
//run方法实现 runnable 接口
LazySimpleSyncSingleton singleton = LazySimpleSyncSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + singleton);
}
}
LazySimpleSyncSingleton
public class LazySimpleSyncSingleton {
private LazySimpleSyncSingleton() {
}
private static LazySimpleSyncSingleton instance = null;
public synchronized static LazySimpleSyncSingleton getInstance() {
//通过给 getInstance() 方法加上 synchronized 关键字,使这个方法编程线程同步方法:
if (instance == null) {
instance = new LazySimpleSyncSingleton();
}
return instance;
}
}
2、测试代码
public class LazySingletonTest {
public static void main(String[] args) {
//创建线程 调用run()方法
//线程不安全的
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t2.start();
System.out.println("线程不安全");
t1.start();
Thread t3 = new Thread(new ExectorThreadSycr());
Thread t4 = new Thread(new ExectorThreadSycr());
//synchronized 加锁时,在线程数量比较多的情况下,
//如果 CPU 分配压力上升,则会导致大批线程阻塞,
//从而导致程序性能大幅下降。
t3.start();
System.out.println("线程安全的,但是有性能问题");
t4.start();
}
}
懒汉单例模式(双重检查)
1、示例代码
ExectorThreadDc
public class ExectorThreadDc implements Runnable {
@Override
public void run() {
//run方法实现 runnable 接口
LazyDoubleCheckSingleton singleton = LazyDoubleCheckSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + singleton);
}
}
LazyDoubleCheckSingleton
public class LazyDoubleCheckSingleton {
private LazyDoubleCheckSingleton() {}
private volatile static LazyDoubleCheckSingleton instance = null;
public static LazyDoubleCheckSingleton getInstance() {
//双重检查锁
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (instance == null) {
instance = new LazyDoubleCheckSingleton();// error
}
}
}
return instance;
}
}
2、测试代码
public class LazySingletonTest {
public static void main(String[] args) {
//创建线程 调用run()方法
//线程不安全的
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("线程不安全");
Thread t3 = new Thread(new ExectorThreadSycr());
Thread t4 = new Thread(new ExectorThreadSycr());
//synchronized 加锁时,在线程数量比较多的情况下,
//如果 CPU 分配压力上升,则会导致大批线程阻塞,
//从而导致程序性能大幅下降。
System.out.println("线程安全的,但是有性能问题");
t3.start();
t4.start();
//重排序问题
//需要在 instance 前加入关键字 volatile。使用了 volatile 关键字后,
//重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前。
Thread t5 = new Thread(new ExectorThreadDc());
Thread t6 = new Thread(new ExectorThreadDc());
t5.start();
t6.start();
System.out.println("double check");
}
}
注册式单例模式
注册式单例模式
注册式单例又称为登记式单例,就是将每一个实例都登记到某一个地方,
使用唯一的标识获取实例。
注册式单例有两种写法:
一种为枚举式单例模式,
一种为容器式单例模式。
枚举单例
1、示例代码
EnumSingleton
//这是一个枚举类型
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
2、测试代码
import java.io.*;
public class EnumSingletonTest {
public static void main(String[] args) {
EnumSingleton instance1 = null;
EnumSingleton instance2 = EnumSingleton.getInstance();
instance2.setData(new Object());
try {
//序列化
FileOutputStream fos = new FileOutputStream("EnumSingletonTest.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance2);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("EnumSingletonTest.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
instance1 = (EnumSingleton) ois.readObject();
ois.close();
System.out.println(instance1.getData());
System.out.println(instance2.getData());
System.out.println(instance1.getData() == instance2.getData());
} catch (Exception e) {
e.printStackTrace();
}
}
}
为什么枚举式单例模式能够避免反射对单例模式的破坏?
枚举的详细说明—源码分析
容器单例
1、示例代码
Pojo
public class Pojo {
}
package com.Design.Singleton.RegisSing;
import java.util.*;
import java.util.concurrent.*;
/*
*容器式单例用Map实现
*一般以类名
*实例名为键值对存放进私有Map中
*容器式单例适用于管理的实例非常多的情况
*非线程安全。
*/
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getInstance(String className){
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
//装载类 包名+类名
obj = Class.forName(className).newInstance();
//put 到 Map 中
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(className);
}
}
}
}
ContainerSingleThread
public class ContainerSingleThread implements Runnable{
@Override
public void run() {
//获取实例对象
Pojo instance= (Pojo) ContainerSingleton.getInstance("com.Design.Singleton.RegisSing.Pojo");
System.out.println(Thread.currentThread().getName()+"--->"+instance);
}
}
2、测试代码
public class ContainerSingleTest {
public static void main(String[] args) {
Thread t1 = new Thread(new ContainerSingleThread());
Thread t2 = new Thread(new ContainerSingleThread());
t1.start();
t2.start();
}
}
线程单例实现
- 线程单例使用 ThreadLocal 来实现。
- ThreadLocal 不能保证其创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全。
1、示例代码
ThreadLocalSigleton
public class ThreadLocalSigleton {
private static final ThreadLocal<ThreadLocalSigleton> threadLocalInstance
= new ThreadLocal<ThreadLocalSigleton>(){
@Override
//java.lang.ThreadLocal.initialValue()方法
//返回此线程局部变量的当前线程的initial value
protected ThreadLocalSigleton initialValue() {
return new ThreadLocalSigleton();
}
};
//私有构造器
private ThreadLocalSigleton(){};
public static ThreadLocalSigleton getInstance(){
return threadLocalInstance.get();
}
}
2、测试代码
public class ThreadLocalSigletonTest {
public static void main(String[] args) {
ThreadLocalSigleton a = ThreadLocalSigleton.getInstance();
ThreadLocalSigleton a1 = ThreadLocalSigleton.getInstance();
ThreadLocalSigleton a2 = ThreadLocalSigleton.getInstance();
ThreadLocalSigleton a3 = ThreadLocalSigleton.getInstance();
System.out.println(a);
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
}
}
我们发现,在主线程中无论调用多少次,获得到的实例都是同一个;在多线程环境下,每个线程获取到了不同的实例。
总结
单例模式为了达到线程安全的目的,会给方法上锁,以时间换空间。
ThreadLocal 将所有的对象放在 ThreadLocalMap 中,为每个线程都提供一个对象,这实际上是以空间换时间来实现线程间隔离的。