1 懒汉模式
此种最简单、方便,缺点可以忽略,<font color = 'red'>建议使用</font>
package com.xin.demo.sigle;
/**
* 懒汉模式,简单实用,推荐使用这种写法
* 类加载到内存后就实例化一个对象,jvm保证线程的安全
* 缺点:不管是否使用,类加载时就进行实例化操作
*/
public class Single01 {
/**
* 类加载时进行对象的创建,jvm 保证类线程安全
*/
private static final Single01 INSTANCE = new Single01();
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single01() {
}
public static Single01 getINSTANCE() {
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single01.getINSTANCE().hashCode());
}).start();
}
}
}
2 同1 静态代码块的写法
package com.xin.demo.sigle;
/**
* 同01
*/
public class Single02 {
/**
* 类加载时进行对象的创建,jvm 保证类线程安全
*/
private static final Single02 INSTANCE;
// 同01 只是改成了 静态代码块
static {
INSTANCE = new Single02();
}
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single02() {
}
public static Single02 getINSTANCE() {
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single02.getINSTANCE().hashCode());
}).start();
}
}
}
3 饿汉模式(非线程安全)
按需分配,此方法存在线程安全问题
package com.xin.demo.sigle;
/**
* 饿汉模式,按需分配
*/
public class Single03 {
private static Single03 INSTANCE = null;
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single03() {
}
public static Single03 getINSTANCE() {
if (INSTANCE == null) {
// 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single03();
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single03.getINSTANCE().hashCode());
}).start();
}
}
}
4 饿汉模式(线程安全)
此处采用给方法上加 synchronized
关键字解决了线程安全问题,同时带来了性能降低的问题,每次获取实例时都会进行等待。
package com.xin.demo.sigle;
/**
* 饿汉模式,按需分配
*/
public class Single04 {
private static Single04 INSTANCE = null;
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single04() {
}
public static synchronized Single04 getINSTANCE() {
// 加锁 保证线程安全,但是这种方式带来了 效率下降的问题
if (INSTANCE == null) {
// 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Single04();
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single04.getINSTANCE().hashCode());
}).start();
}
}
}
5 饿汉模式(解决性能问题)
此种存在问题,通过代码块未达到目的,时6 的过渡。
package com.xin.demo.sigle;
/**
* 饿汉模式,按需分配
*/
public class Single05 {
private static Single05 INSTANCE = null;
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single05() {
}
public static Single05 getINSTANCE() {
if (INSTANCE == null) {
// 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
synchronized (Single05.class) {
// 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
INSTANCE = new Single05();
}
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single05.getINSTANCE().hashCode());
}).start();
}
}
}
6 饿汉模式(完美的饿汉模式)
package com.xin.demo.sigle;
/**
* 饿汉模式,按需分配
*/
public class Single06 {
private static Single06 INSTANCE = null;
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single06() {
}
public static Single06 getINSTANCE() {
// 为了提高效率,如果为null 则不进锁
if (INSTANCE == null) {
// 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
synchronized (Single06.class) {
// 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
// 因此 此处需要在进行一次判断,双重判断
if (INSTANCE == null) {
INSTANCE = new Single06();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single06.getINSTANCE().hashCode());
}).start();
}
}
}
7 通过内部类实现
package com.xin.demo.sigle;
/**
* 通过内部类 来实现加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Single07 {
// 内部类不会在类加载的时候加载,只有在调用的时候进行加载,因此是按需分配,jvm保证了线程的安全
public static class SingleHolder {
protected static Single07 INSTANCE = new Single07();
}
/**
* 构造器必须私有化,不允许外部进行对象的创建
*/
private Single07() {
}
public static Single07 getINSTANCE() {
return SingleHolder.INSTANCE;
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single07.getINSTANCE().hashCode());
}).start();
}
}
}
8 通过枚举实现
effective java 中建议采用 枚举来实习单例 ,<font color='red'>推荐使用</font>
package com.xin.demo.sigle;
/**
* 通过 枚举保证了线程的安全,也可以防止反序列化
*/
public enum Single08 {
INSTANCE;
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single08.INSTANCE.hashCode());
}).start();
}
}
}
9 通过枚举实现
枚举可以实现接口,无法进行继承,大多数情况下还是需要用到类的,因此我们通过在类中嵌套枚举以达到单列。
package com.xin.demo.sigle;
/**
* 通过 枚举保证了线程的安全,也可以防止反序列化
*
* 枚举没法进行 继承,因此有时候通常需要类来进行操作,此处是将类 通过枚举来实现单列
*/
public class Single09 {
public static Single09 getINSTANCE() {
return Single09Enum.SINGLE09.getInstance();
}
public enum Single09Enum {
SINGLE09;
private final Single09 INSTANCE;
Single09Enum() {
INSTANCE = new Single09();
}
public Single09 getInstance() {
return INSTANCE;
}
}
public static void main(String[] args) {
System.out.println(2);
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Single09.getINSTANCE().hashCode());
}).start();
}
}
}