前言:设计模式源于生活
单例的基本概念
单例模式确保某各类只有一个实例,而且自行实例化并向整个系统提供这个实例。选择单例模式就是为了避免不一致状态
单例模式特点
1、单例类只能有一个实例。
2、单例类必须自己创建自己唯一的实例。
3、单例类必须给所有其它对象提供这一实例。
单例模式的优缺点
1、单例类只有一个实例
2、共享资源,全局使用
3、节省创建时间,提高性能
单例模式应用场景
项目定义的配置文件都是单例
SpringIOC容器默认就是单例模式
线程池和数据库连接池都是单例
饿汉模式
public class SingletonV1 {
/**
* 当类初始化,就会创建该对象
*/
private static SingletonV1 singletonV1 = new SingletonV1();
/**
* 构造方法需要私有化,禁止初始化
*/
private SingletonV1() {
}
public static SingletonV1 getInstance() {
return singletonV1;
}
/**
* 测试单例
*
* @param args
*/
public static void main(String[] args) {
SingletonV1 instance1 = SingletonV1.getInstance();
SingletonV1 instance2 = SingletonV1.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
饿汉模式-静态代码块
public class SingletonV5 {
private static SingletonV5 singletonV5 = null;
//类加载读取
static {
singletonV5 = new SingletonV5();
}
public static SingletonV5 getInstance(){
return singletonV5;
}
public static void main(String[] args) {
SingletonV5 instance1 = SingletonV5.getInstance();
SingletonV5 instance2 = SingletonV5.getInstance();
System.out.println(instance1 == instance2);
}
}
懒汉模式-线程不安全
public class SingletonV2 {
private static SingletonV2 singleton001;
/**
* 构造方法需要私有化,禁止初始化
*/
private SingletonV2() {
}
/**
* 多线程下,可能会造成线程不安全,违背单例模式
* @return
*/
public static SingletonV2 getInstance() {
if (null == singleton001) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton001 = new SingletonV2();
}
return singleton001;
}
/**
* 测试单例
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
public void run() {
SingletonV2 instance = SingletonV2.getInstance();
System.out.println(instance);
}
}).start();
}
}
}
懒汉模式-线程安全
public class SingletonV3 {
private static SingletonV3 singletonV2;
private SingletonV3() {
}
/**
* 效率低,每次获取单例都需要上锁
* @return
*/
public static synchronized SingletonV3 getInstance() {
if (null == singletonV2) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singletonV2 = new SingletonV3();
}
return singletonV2;
}
/**
* 测试单例
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
public void run() {
SingletonV3 instance = SingletonV3.getInstance();
System.out.println(instance);
}
}).start();
}
}
}
双重检验锁
public class SingletonV4 {
private static SingletonV4 singletonV4;
private SingletonV4() {
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV4 getInstance() {
if (null == singletonV4) {
synchronized (SingletonV4.class) {
if (null == singletonV4) {
singletonV4 = new SingletonV4();
}
}
}
return singletonV4;
}
public static void main(String[] args) {
SingletonV4 instance1 = SingletonV4.getInstance();
SingletonV4 instance2 = SingletonV4.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
破解单例模式-基于双重检验锁
public class SingletonV7 {
private static SingletonV7 singletonV7;
private SingletonV7() throws Exception {
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV7 getInstance() throws Exception {
if (null == singletonV7) {
synchronized (SingletonV7.class) {
if (null == singletonV7) {
singletonV7 = new SingletonV7();
}
}
}
return singletonV7;
}
public static void main(String[] args) throws Exception {
//获取单例对象
SingletonV7 instance1 = SingletonV7.getInstance();
//通过反射创建对象,暴力破解单例模式
Class> aClass = Class.forName("com.dream.sunny.SingletonV7");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV7 instance2 = (SingletonV7) declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1 == instance2);
}
}
可以发现,我第一次获取完对象后,第二次通过反射的形式,来创建对象,所以变相的破解了单例模式
双重检验锁-三级校验
public class SingletonV8 {
private static SingletonV8 singletonV8;
private SingletonV8() throws Exception {
synchronized (SingletonV8.class){
if(singletonV8 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV8 getInstance() throws Exception {
if (null == singletonV8) {
synchronized (SingletonV8.class) {
if (null == singletonV8) {
singletonV8 = new SingletonV8();
}
}
}
return singletonV8;
}
public static void main(String[] args) throws Exception {
//获取单例对象
SingletonV8 instance1 = SingletonV8.getInstance();
// //通过反射创建对象,暴力破解单例模式
Class> aClass = Class.forName("com.dream.sunny.SingletonV8");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV8 instance2 = (SingletonV8) declaredConstructor.newInstance();
}
}
因为反射是需要通过无参构造方法进行创建对象,所以可以在无参构造方法,多加一层校验,判断单例对象是否存在
双重检验锁-三级校验-再次破坏
public class SingletonV9 {
private static SingletonV9 singletonV9;
private SingletonV9() throws Exception {
synchronized (SingletonV9.class){
if(singletonV9 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV9 getInstance() throws Exception {
if (null == singletonV9) {
synchronized (SingletonV9.class) {
if (null == singletonV9) {
singletonV9 = new SingletonV9();
}
}
}
return singletonV9;
}
public static void main(String[] args) throws Exception {
//通过反射创建对象,暴力破解单例模式
Class> aClass = Class.forName("com.dream.sunny.SingletonV9");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV9 instance2 = (SingletonV9) declaredConstructor.newInstance();
//获取单例对象
SingletonV9 instance1 = SingletonV9.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
双重检验锁-三级校验升级版
public class SingletonV10 {
private static SingletonV10 singletonV10;
private SingletonV10() throws Exception {
synchronized (SingletonV10.class){
if(singletonV10 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}else{
singletonV10 = this;
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV10 getInstance() throws Exception {
if (null == singletonV10) {
synchronized (SingletonV10.class) {
if (null == singletonV10) {
singletonV10 = new SingletonV10();
}
}
}
return singletonV10;
}
public static void main(String[] args) throws Exception {
//通过反射创建对象,暴力破解单例模式
Class> aClass = Class.forName("com.dream.sunny.SingletonV10");
Constructor> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV10 instance2 = (SingletonV10) declaredConstructor.newInstance();
//获取单例对象
SingletonV10 instance1 = SingletonV10.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举类,实现单例模式(枚举实现单例模式是无法被破解的,因为枚举没有空的构造方法)
public enum SingletonV11 {
INSTANCE;
SingletonV11(){
System.out.println("初始化方法");
}
public static void main(String[] args) {
SingletonV11 instance1 = SingletonV11.INSTANCE;
SingletonV11 instance2 = SingletonV11.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
总结
到这里几种写法都介绍完了,至于选择用哪种形式的单例模式,取决于你的项目本身,是否是有复杂的并发环境,还是需要控制单例对象的资源消耗。