饿汉模式
提到"饿汉模式",相信很多人跟我一样一脸懵逼,为什么叫"饿汉"?为了清楚理解"饿汉模式",请允许我给你们介绍另一个概念: “立即加载”.什么是立即加载?立即加载就是我们在使用类的时候已经将对象创建完毕,常见的的实现方式就是直接new对象.而立即加载从语义来看,有着"急切,马上"的含义,所以称为"饿汉模式".
"饿汉模式"是在调用方法之前,实例对象已经被创建.接下来用代码进行演示
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
| package com.xiebin.day5;
public class SingletonTest {
private static SingletonTest singletonTest = new SingletonTest();
private SingletonTest(){
}
public static SingletonTest getInstance(){
return singletonTest; } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| package com.xiebin.day5;
public class MyThread extends Thread{ @Override public void run() { System.out.println(SingletonTest.getInstance().hashCode()); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.xiebin.day5;
public class MyRun { public static void main(String[] args) {
MyThread m1 = new MyThread(); MyThread m2 = new MyThread(); MyThread m3 = new MyThread(); m1.start(); m2.start(); m3.start(); } }
|
懒汉模式
延迟加载就是在调用get()方法时实例才被创建.常见方法就是在get方法中进行new对象.
单线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.xiebin.day5;
public class MySingleton { private static MySingleton mySingleton; private MySingleton(){
}
public static MySingleton getInstance(){ if (mySingleton == null) { mySingleton = new MySingleton(); } return mySingleton; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| package com.xiebin.day5;
public class MyThread extends Thread { @Override public void run() { System.out.println(MySingleton.getInstance().hashCode()); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.xiebin.day5;
public class MyRun {
public static void main(String[] args) { MyThread m1 = new MyThread(); MyThread m2 = new MyThread(); MyThread m3 = new MyThread(); m1.start(); m2.start(); m3.start(); }
} -------------------------------------------------- 连接到目标 VM,地址:'127.0.0.1:51282', transport: 'socket' 767501637 767501637 767501637 断开与目标 VM 的连接,地址:'127.0.0.1:51282', transport: 'socket'
|
多线程
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
| package com.xiebin.day5;
public class MySingleton { private static MySingleton mySingleton; private MySingleton(){
}
public static MySingleton getInstance(){ if (mySingleton == null) { try { Thread.sleep(3000); mySingleton = new MySingleton(); } catch (InterruptedException e) { e.printStackTrace(); } } return mySingleton; }
} ---------------------------------- 453594667 704737505 1372676747
|
在多线程环境下,控制台打印了3个不同的hashcode值,说明创建了三个不同的对象,违背了单例模式的本意
优化方法
声明synchronize关键字
既然多个线程可以同时访问getInstance()方法,那么我们在这个方法上加把锁,在一段时间内只能有一个线程访问getInstance()方法.
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
| package com.xiebin.day5;
public class MySingleton { private static MySingleton mySingleton;
private MySingleton() {
}
synchronized public static MySingleton getInstance() { try { if (mySingleton == null) { Thread.sleep(3000); mySingleton = new MySingleton(); } } catch (InterruptedException e) { e.printStackTrace(); } return mySingleton; }
} ---------------------------------- 1287094394 1287094394 1287094394
|
上述方式是同步运行的,下一个线程要取得对象,就必须等上一个线程释放锁之后,才能继续执行,.效率低下.继续改写代码解决这个缺点
同步代码块
同步代码块可以针对某些重要的代码进行单独的同步,这样效率可以大幅度提升.
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
| package com.xiebin.day5;
public class MySingleton { private static MySingleton mySingleton;
private MySingleton() {
} public static MySingleton getInstance() { try { if (mySingleton == null) { Thread.sleep(3000); synchronized (MySingleton.class) { mySingleton = new MySingleton(); } } } catch (InterruptedException e) { e.printStackTrace(); } return mySingleton; }
} ----------------------------- 547686109 1391273746 738978376
|
使用DCL双检查锁机制
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
| package com.xiebin.day5;
public class MySingleton { private static MySingleton mySingleton;
private MySingleton() { }
public static MySingleton getInstance() { try { if (mySingleton == null) { Thread.sleep(3000); synchronized (MySingleton.class) { if (mySingleton == null) { mySingleton = new MySingleton(); } } } } catch (InterruptedException e) { e.printStackTrace(); } return mySingleton; } }
|
由于DCL无法解决指令重排的问题,需要关键字volatile禁止指令重排,在多线程环境下,使用DCL双检锁+volatile结合才能符合单例模式的要求
使用静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.xiebin.day5;
public class MySingleton { private static class MyHolder { private static MySingleton mySingleton = new MySingleton(); }
private MySingleton() {
}
public static MySingleton getInstance(){ return MyHolder.mySingleton; } } ------------------------ 704737505 704737505 704737505
|
序列化与反序列化
未完待续…
使用static代码块
特性: 利用static 带块随着类的加载而执行且只执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.xiebin.day5;
public class MySingleton { private static MySingleton instance = null;
private MySingleton(){
}
static { instance = new MySingleton(); }
public static MySingleton getInstance(){ return instance; } }
|
使用枚举类
特性: 在使用枚举类时,构造方法会被自动调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.xiebin.day5;
public class MySingleton { private MySingleton(){
} private enum Singleton{ INSTANCE; private final MySingleton instance; Singleton(){ instance = new MySingleton(); } private MySingleton getInstance(){ return instance; } } public static MySingleton getInstance(){ return Singleton.INSTANCE.getInstance(); } }
|