单例模式
- 实现方式:把构造方法私有化,不让外部进行访问,在类中创建私有化对象,调用类中的公有方法返回对象或创建对象
- 意图:节省资源 避免一个对象频繁的创建与销毁
以下总结单例模式的七种实现方式
1.饿汉式
顾名思义:我想去创建(拥有)一个对象------类加载的时候就创建对象
创建对外提供方法返回对象
- 好处:线程安全(类加载机制),没有加锁=执行效率要高
- 缺点:在类加载的时候创建对象,若用不上的该对象,会浪费资源(占空间)
对于反射和反序列化是不安全的
//实现代码
public class Singleton_01 implements Serializable {
private Singleton_01(){};
private static volatile Singleton_01 singleton_01 = new Singleton_01();
public static Singleton_01 getInstance(){
return singleton_01;
}
//因为静态的变量在序列化过程中是不会被保存的,所以在反序列化时会重新生成实例
private Object readResolve(){
//反序列化内部其实利用的还是反射,目标类存在readResolve时,做的是一个浅拷贝
/*如果单例对象中定义了readResolve()方法,则对前面生成的对象进行覆盖,来保证单例。
实际上实例化了两次,只不过第二次实例化的对象没有被返回而已*/
return singleton_01;
}
}
//在测试类中测试
//true
@Test
public void text01() {
Singleton_01 instance = Singleton_01.getInstance();
Singleton_01 instance1 = Singleton_01.getInstance();
System.out.println(instance.equals(instance1));
}
//饿汉式 反射不安全
//false
@Test
public void text001() throws Exception {
Class clazz = Singleton_01.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton_01 instance = Singleton_01.getInstance();
Singleton_01 instance1 = (Singleton_01) constructor.newInstance();
System.out.println(instance==instance1);
}
//饿汉式 反序列化不安全 添加readResolve()可以使其变得安全
//测试先执行序列化,然后执行反序列化 结果为 true
@Test
public void test0001(){
Singleton_01 s1 = Singleton_01.getInstance();
SerializeUtil.serialize(s1);
/*Singleton_01 s1 = (Singleton_01) SerializeUtil.unserialize();
Singleton_01 s2 = (Singleton_01) SerializeUtil.unserialize();
System.out.println(s1==s2);*/
}
2.懒汉式
顾名思义:我想去懒得(拥有)一个对象(lazy loading)------类加载的时候没有创建对象
创建对外提供方法返回对象
- 好处:再调用对外提供的方法时,创建对象,不会浪费空间
- 缺点:线程不安全
public class Singleton_02 {
private Singleton_02(){}
private static Singleton_02 singleton_02 = null;
public static /*synchronized*/ Singleton_02 getInstance(){
synchronized (Singleton_02.class){
//同步代码块
}
if (singleton_02==null) {
/*在这里时容易产生线程不安全*/
/* eg:
* 线程1判定为空后sleep了一会,线程2此时进来了
*
* 解决方式:可在对外提供的方法上加入synchronized关键字保证线程安全
* ---不过也有缺点,因为加了synchronized肯定会导致效率变低(很小)
*/
singleton_02 = new Singleton_02();
}
return singleton_02;
}
}
//懒汉式单线程
//true
@Test
public void text02() {
Singleton_02 instance = Singleton_02.getInstance();
Singleton_02 instance1 = Singleton_02.getInstance();
System.out.println(instance.equals(instance1));
}
3.懒汉式 双检索——可以看成升级版的懒汉
小几率指令重排
需要加上volatile
singleton_02 = new Singleton_002(); 会执行如下操作:
1.分配对象内存空间
2.初始化对象
3.singleton_02指向 1 中分配的空间
在某些编译器上,可能出现指令重排:
1.分配对象内存空间
2.singleton_02指向 1 中分配的空间(但此时对象没有初始化)
3.初始化对象
public class Singleton_002 {
private Singleton_002() {
}
private static volatile Singleton_002 singleton_02 = null;
public static /*synchronized*/ Singleton_002 getInstance() {
//在第一重发现不为null 不会向下继续执行同步代码块
//如果不加 则每一次都会进入同步 效率低
if (singleton_02 == null) {
synchronized (Singleton_002.class) {
//同步代码块
if (singleton_02 == null) {
singleton_02 = new Singleton_002();
}
}
}
return singleton_02;
}
}
//懒汉式多线程
@Test
public void text002() {
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Singleton_02.getInstance());
}
}).start();
}
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
4.静态内部类
通过类加载的机制创建对象(静态内部类)
创建一个私有内部类,再类中对外部类的创建,并私有化
一个公开的方法,返回内部类所创建的对象
- 好处:由于类加载器加载时的安全机制,所以内部类所创建的对象是安全的
public class Singleton_03 {
private Singleton_03(){
//禁用反射 防止反射攻击
if (SingletonHolder.SINGLETON_03!=null){
throw new IllegalStateException();
}
};
public static class SingletonHolder{
private static final Singleton_03 SINGLETON_03 = new Singleton_03();
}
public static final Singleton_03 getInstance(){
return SingletonHolder.SINGLETON_03;
}
}
//静态内部类 登记式
//true
@Test
public void test03() {
Singleton_03 instance = Singleton_03.getInstance();
Singleton_03 instance1 = Singleton_03.getInstance();
System.out.println(instance.equals(instance1));
}
//普通登记式无法保证反射安全
//抛出异常
@Test
public void test003() throws Exception {
Class clazz = Singleton_03.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton_03 instance1 = (Singleton_03) constructor.newInstance();
Singleton_03 instance = Singleton_03.getInstance();
System.out.println(instance == instance1);
}
5.枚举式
简单高效
线程安全
不是延迟初始化的,立即初始化
自动支持序列化,防止反序列化创建一个新的对象
防止反射攻击
public enum Singleton_04 {
INSTANCE{
@Override
protected void doSomething() {
}
};
protected abstract void doSomething();
}
//枚举式
@Test
public void test04(){
Singleton_04 instance = Singleton_04.INSTANCE;
Singleton_04 instance1 = Singleton_04.INSTANCE;
System.out.println(instance==instance1);
}
//对于反射默认就是安全的
@Test
public void test004() throws Exception {
Class<Singleton_04> aClass = Singleton_04.class;
Constructor<Singleton_04> constructor = aClass.getConstructor();
Singleton_04 singleton_04 = constructor.newInstance();
Singleton_04 instance = Singleton_04.INSTANCE;
System.out.println(singleton_04==instance);
}
//枚举 序列化
@Test
public void test0004() throws Exception {
/*Singleton_04 instance = Singleton_04.INSTANCE;
SerializeUtil.serialize(instance);*/
Singleton_04 unserialize = (Singleton_04) SerializeUtil.unserialize();
Singleton_04 unserialize1 = (Singleton_04) SerializeUtil.unserialize();
System.out.println(unserialize==unserialize1);
}
6.通过ThreadLocal
public class Singleton_05 {
private static Singleton_05 instance = null;
private Singleton_05(){}
private static final ThreadLocal<Singleton_05> theadLocalSingleton = new ThreadLocal<Singleton_05>(){
@Override
protected Singleton_05 initialValue() {
return new Singleton_05();
}
};
public static Singleton_05 getInstance(){
return theadLocalSingleton.get();
}
}
//创建单例模式 单线程
@Test
public void test05(){
Singleton_05 instance = Singleton_05.getInstance();
Singleton_05 instance1 = Singleton_05.getInstance();
System.out.println(instance==instance1);
}
//创建单例模式 多线程
@Test
public void test005(){
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Singleton_05 instance = Singleton_05.getInstance();
Singleton_05 instance1 = Singleton_05.getInstance();
System.out.println(Thread.currentThread().getName()+(instance==instance1)+" "+instance);
}
}).start();
}
}
7.CAS
CAS Compare And Swap 比较并替换。
CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作
如果因为冲突失败就重试,直到成功为止
public class Singleton_06 {
//原子类
private static final AtomicReference<Singleton_06> instance = new AtomicReference<>();
private Singleton_06(){
System.out.println("Singleton_06 loaded");
}
public static final Singleton_06 getInstance(){
while (true) {
Singleton_06 current = instance.get();
if (current != null) {
return current;
}
current = new Singleton_06();
if (instance.compareAndSet(null, current)) {
return current;
}
}
}
}
@Test
public void test06() throws IOException {
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Singleton_06.getInstance());
}
}).start();
}
System.in.read();
}