文章目录
单例模式的定义与特点
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
特点
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
单例模式的优点和缺点
优点
- 减少内存开销
- 单例模式设置全局访问点,可以优化和共享资源的访问
缺点
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的应用场景
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
- 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
单例模式实现
1.饿汉加载模式
/**
* 饿汉加载模式
* JVM保证线程安全
* 缺点:不管类是否被用到,类装载时就被实例化
*/
public class SingletonTest01 {
private final static SingletonTest01 singletonTest01 = new SingletonTest01();
//构造私有化,防止别人new对象
private SingletonTest01() {
}
//static修饰(被static修饰的方法和变量不需要依赖对象来进行访问,只要类被加载了,就能通过类名直接调用)
public static SingletonTest01 getInstance() {
return singletonTest01;
}
public static void main(String[] args) {
SingletonTest01 instance1 = SingletonTest01.getInstance();
SingletonTest01 instance2 = SingletonTest01.getInstance();
System.out.println(instance1 == instance2);
}
}
2.懒汉加载模式–同步方法
/**
* 懒汉模式
* 同步方法,解决线程安全问题,带来效率问题
*/
public class SingletonTest02 {
private static SingletonTest02 singletonTest;
//构造私有化,防止别人new对象
private SingletonTest02() {
}
//static修饰(被static修饰的方法和变量不需要依赖对象来进行访问,只要类被加载了,就能通过类名直接调用)
//synchronized加锁
public static synchronized SingletonTest02 getInstance() {
if (singletonTest == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
singletonTest = new SingletonTest02();
}
return singletonTest;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonTest02.getInstance().hashCode());
}).start();
}
}
}
3.懒汉加载模式–同步代码块-线程不安全
/**
* 懒汉模式
* 同步代码块,解决同步方法带来的效率问题,但是引发线程不安全
*/
public class SingletonTest03 {
private static SingletonTest03 singletonTest;
//构造私有化,防止别人new对象
private SingletonTest03() {
}
//static修饰(被static修饰的方法和变量不需要依赖对象来进行访问,只要类被加载了,就能通过类名直接调用)
public static SingletonTest03 getInstance() {
//线程AB同时走到当前判断,线程A拿到锁,继续new,释放锁,线程B拿到锁,继续new,这时候会产生两个不同实例
if (singletonTest == null) {
//同步方法改为同步代码块,降低锁的范围
synchronized (SingletonTest03.class) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
singletonTest = new SingletonTest03();
}
}
return singletonTest;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonTest03.getInstance().hashCode());
}).start();
}
}
}
4.懒汉模式–双重检查
/**
* 懒汉模式
* 双重检查
*/
public class SingletonTest04 {
private static SingletonTest04 singletonTest;
//构造私有化,防止别人new对象
private SingletonTest04() {
}
//static修饰(被static修饰的方法和变量不需要依赖对象来进行访问,只要类被加载了,就能通过类名直接调用)
public static SingletonTest04 getInstance() {
//加入双重检查代码,解决线程安全问题,同时解决懒加载问题
if (singletonTest == null) {
synchronized (SingletonTest04.class) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (singletonTest == null) {
singletonTest = new SingletonTest04();
}
}
}
return singletonTest;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonTest04.getInstance().hashCode());
}).start();
}
}
}
5.懒汉加载模式–静态内部类
/**
* 懒汉模式
* 静态内部类
* 静态内部类方式在SingletonTest05类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonTest05Holder类,从而完成SingletonTest05Holder的实例化。
*/
public class SingletonTest05 {
private volatile static SingletonTest05 singletonTest;
//构造私有化,防止别人new对象
private SingletonTest05() {
}
//static修饰(被static修饰的方法和变量不需要依赖对象来进行访问,只要类被加载了,就能通过类名直接调用)
public static SingletonTest05 getInstance() {
return SingletonTest05Holder.singleton;
}
private static class SingletonTest05Holder {
private static final SingletonTest05 singleton = new SingletonTest05();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonTest05.getInstance().hashCode());
}).start();
}
}
}
3.懒汉加载模式–枚举
/**
* 懒汉模式
* 枚举
*/
public enum SingletonTest06 {
INSTANCE;
public void testMethod() {
System.out.println("主业务。。。");
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonTest06.INSTANCE.hashCode());
}).start();
}
}
}