单例模式
单例模式,顾名思义就是一个类只有一个实例。
单例主要的好处就是,1:可以解决资源访问冲突的问题。2:减少资源浪费。
单例的实现方式主要有以下几种。
1:饿汉式
在类加载的时候就实力化对象,不支持延迟加载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| public class HungryDemo {
private AtomicInteger id = new AtomicInteger(0);
public static HungryDemo instance = new HungryDemo();
private HungryDemo() {
}
public static HungryDemo getInstance() {
return instance;
}
public int getId() {
return id.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
HungryDemo instance = HungryDemo.getInstance();
System.out.println(instance + "==>" + instance.getId());
}).start();
}
}
}
|
2:懒汉式
支持延迟加载,在使用的时候才真正加载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| public class LazyDemo {
private AtomicInteger id = new AtomicInteger(0);
public static LazyDemo instance;
private LazyDemo() {
}
public static synchronized LazyDemo getInstance() {
if (Objects.isNull(instance)) {
instance = new LazyDemo();
}
return instance;
}
public int getId() {
return id.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
LazyDemo instance = LazyDemo.getInstance();
System.out.println(instance + "==>" + instance.getId());
}).start();
}
}
}
|
饿汉和懒汉的区别就是一个支持延迟加载,一个不支持。懒汉模式还要加锁,防止并发问题。从这一点来看,懒汉模式性能是不如饿汉模式,饿汉模式在类加载时就进行实力化,也可以提前将实力化过程中发生的资源不足等问题提前暴露,而不是等到业务访问后才发现无法进行初始化,引发线上事故。
但这两种都有各自的不足,所以现在有第三种方式。
3:双重检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| public class DoubleCheckDemo {
private AtomicInteger id = new AtomicInteger(0);
public static DoubleCheckDemo instance;
private DoubleCheckDemo() {
}
public static DoubleCheckDemo getInstance() {
if (Objects.isNull(instance)) {
synchronized (DoubleCheckDemo.class) {
System.out.println("加锁操作");
if (Objects.isNull(instance)) {
instance = new DoubleCheckDemo();
}
}
}
return instance;
}
public int getId() {
return id.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
DoubleCheckDemo instance = DoubleCheckDemo.getInstance();
System.out.println(instance + "==>" + instance.getId());
}).start();
}
}
}
|
双重检测解决了懒汉模式的并发性能问题,同时支持懒加载。
4:静态内部类
静态内部类的实现比双重检测要更加简单,同时也能做到懒加载。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| public class InnerClassDemo {
private AtomicInteger id = new AtomicInteger(0);
private InnerClassDemo() {
}
private static class InnerClassDemoHolder {
private static final InnerClassDemo instance = new InnerClassDemo();
}
public static InnerClassDemo getInstance() {
return InnerClassDemoHolder.instance;
}
public int getId() {
return id.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
InnerClassDemo instance = InnerClassDemo.getInstance();
System.out.println(instance + "==>" + instance.getId());
}).start();
}
}
}
|
InnerClassDemoHolder
是静态内部类,一开始并不会加载,等到调用getInstance()
方法时才会被加载,这是一种无锁的实现,线程安全由jvm来保证。
5:枚举
采用枚举的方式来实现是最简单的方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| public enum EnumDemo {
INSTANCE;
private AtomicInteger id = new AtomicInteger(0);
public int getId() {
return id.incrementAndGet();
}
}
public class EnumDemoTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
EnumDemo instance = EnumDemo.INSTANCE;
System.out.println(instance + "==>" + instance.getId());
}).start();
}
}
}
|
工厂模式