diff --git a/pom.xml b/pom.xml
index d6dc414..9cb920c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,13 @@
2.11.0
+
+
+ org.apache.commons
+ commons-lang3
+ 3.8.1
+
+
diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingleton.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingleton.java
new file mode 100644
index 0000000..8ea79ae
--- /dev/null
+++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingleton.java
@@ -0,0 +1,50 @@
+package top.fjy8018.designpattern.pattern.creational.singleton;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 基于容器(集合)实现单例
+ * 适用于类加载时有多个对象需要实例化场景
+ *
+ * @author F嘉阳
+ * @date 2018-09-24 22:31
+ */
+@Slf4j
+public class ContainerSingleton {
+ private static Map instancesMap = new HashMap<>();
+
+ private ContainerSingleton() {
+ }
+
+ public static void putInstance(String key, Object instance) {
+ if (StringUtils.isNotBlank(key) && instance != null) {
+ if (!instancesMap.containsKey(key)) {
+ instancesMap.put(key, instance);
+ }
+ }
+ }
+
+ public static Object getInstance(String key) {
+ return instancesMap.get(key);
+ }
+
+ /**
+ * 线程安全测试
+ *
+ * @param key
+ * @param instance
+ */
+ public static void putInstance2(String key, Object instance) throws InterruptedException {
+ if (StringUtils.isNotBlank(key) && instance != null) {
+ if (!instancesMap.containsKey(key)) {
+ Thread.sleep(1000);
+ log.debug("放入实例:" + instance);
+ instancesMap.put(key, instance);
+ }
+ }
+ }
+}
diff --git a/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonHashtable.java b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonHashtable.java
new file mode 100644
index 0000000..a338134
--- /dev/null
+++ b/src/main/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonHashtable.java
@@ -0,0 +1,50 @@
+package top.fjy8018.designpattern.pattern.creational.singleton;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * 基于容器(集合)实现单例(线程不安全)
+ * {@link Hashtable} 实现(不建议)
+ *
+ * @author F嘉阳
+ * @date 2018-09-24 22:31
+ */
+@Slf4j
+public class ContainerSingletonHashtable {
+ private static Map instancesMap = new Hashtable<>();
+
+ private ContainerSingletonHashtable() {
+ }
+
+ public static void putInstance(String key, Object instance) {
+ if (StringUtils.isNotBlank(key) && instance != null) {
+ if (!instancesMap.containsKey(key)) {
+ instancesMap.put(key, instance);
+ }
+ }
+ }
+
+ public static Object getInstance(String key) {
+ return instancesMap.get(key);
+ }
+
+ /**
+ * 线程安全测试
+ *
+ * @param key
+ * @param instance
+ */
+ public static void putInstance2(String key, Object instance) throws InterruptedException {
+ if (StringUtils.isNotBlank(key) && instance != null) {
+ if (!instancesMap.containsKey(key)) {
+ Thread.sleep(1000);
+ log.debug("放入实例:" + instance);
+ instancesMap.put(key, instance);
+ }
+ }
+ }
+}
diff --git a/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonTest.java b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonTest.java
new file mode 100644
index 0000000..10de4f1
--- /dev/null
+++ b/src/test/java/top/fjy8018/designpattern/pattern/creational/singleton/ContainerSingletonTest.java
@@ -0,0 +1,79 @@
+package top.fjy8018.designpattern.pattern.creational.singleton;
+
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+class ContainerSingletonTest {
+
+ /**
+ * 直接使用{@link java.util.HashMap} 只会放入一个值,但后面放的值会覆盖首次放入的值
+ *
+ * @throws InterruptedException
+ */
+ @Test
+ void getInstanceHashMapUnsafe() throws InterruptedException {
+ Thread thread1 = new Thread(new MyHashMapRunnable());
+ Thread thread2 = new Thread(new MyHashMapRunnable());
+
+ thread1.start();
+ thread2.start();
+
+ Thread.sleep(2000);
+ log.debug("finish");
+ }
+
+ /**
+ * 使用{@link java.util.Hashtable} {@link java.util.concurrent.ConcurrentHashMap}可以控制并发,只会放入一个值,但后面放的值会覆盖首次放入的值,效果实际同{@link java.util.HashMap}
+ * 对于Hashtable,在一定程度保证线程安全,但影响了性能
+ * 对于ConcurrentHashMap,由于使用了静态的ConcurrentHashMap,并直接操作了map,也不是绝对线程安全
+ *
+ * @throws InterruptedException
+ */
+ @Test
+ void getInstanceHashtableUnsafe() throws InterruptedException {
+ Thread thread1 = new Thread(new MyHashtableRunnable());
+ Thread thread2 = new Thread(new MyHashtableRunnable());
+
+ thread1.start();
+ thread2.start();
+
+ Thread.sleep(2000);
+ log.debug("finish");
+ }
+
+ @Test
+ void putInstance2() {
+ }
+
+ private class MyHashMapRunnable implements Runnable {
+ @Override
+ public void run() {
+ try {
+ ContainerSingleton.putInstance2("object", new Object());
+ // 等待其他线程放入对象后取值
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Object singleton = ContainerSingleton.getInstance("object");
+ log.info(Thread.currentThread().getName() + " " + singleton);
+ }
+ }
+
+ private class MyHashtableRunnable implements Runnable {
+ @Override
+ public void run() {
+ try {
+ ContainerSingletonHashtable.putInstance2("object", new Object());
+ // 等待其他线程放入对象后取值
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Object singleton = ContainerSingletonHashtable.getInstance("object");
+ log.info(Thread.currentThread().getName() + " " + singleton);
+ }
+ }
+}
\ No newline at end of file