单例模式
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式
特点
- 单例类只有一个实例对象
- 该单例对象必须由单例类自行创建
- 单例类对外提供一个访问该单例的全局访问点
第一种单例(饿汉式)(静态常量)
/**
* 饿汉式
* 类加载初始化时就创建好一个静态的对象供外部使用,JVM保证线程安全
* 缺点:
* 不管用到与否,类加载时就完成实例化
*
*/
public class Singleton01 {
//在类内创建一个静态对象
private static final Singleton01 SINGLETON = new Singleton01();
//让类的构造函数私有化
private Singleton01(){
}
//创建一个公有的静态方法访问这个对象
public static Singleton01 getInstance(){
return SINGLETON;
}
public static void main(String[] args) {
Singleton01 instance1 = Singleton01.getInstance();
Singleton01 instance2 = Singleton01.getInstance();
System.out.println(instance1==instance2);
}
}
第二种单例(饿汉)(静态代码块)
public class Singleton02 {
//使用静态代码块
private static final Singleton01 SINGLETON;
static {
SINGLETON = new Singleton02();
}
//让类的构造函数私有化
private Singleton02(){
}
//创建一个公有的静态方法访问这个对象
public static Singleton02 getInstance(){
return SINGLETON;
}
public static void main(String[] args) {
Singleton02 instance1 = Singleton01.getInstance();
Singleton02 instance2 = Singleton01.getInstance();
System.out.println(instance1==instance2);
}
}
第三种单例(懒汉式)
/**
* 懒汉模式
* 只有当调用getIntance()的时候,才会初始化这个实例
* 线程不安全的,当多个线程同时访问的时候,对象会被实例化多次
*/
public class Singleton03 {
private static Singleton03 instance ;
private Singleton02(){
}
public static Singleton03 getInstance(){
if(instance == null){
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
instance = new Singleton03();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
//使用匿名对象和匿名内部类的形式实现
/* new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Singleton02.getInstance());
}
}).start();*/
//使用Lambda表达式实现
new Thread(()->{
System.out.println(Singleton03.getInstance());
}).start();
}
}
}
测试结果实例化了多个对象
第四种单例懒汉式(线程安全,同步方法)
public class Singleton04 {
private static Singleton04 intance;
private Singleton03(){
}
public synchronized static Singleton04 getIntance(){
if(intance == null){
try {
Thread.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
intance = new Singleton04();
}
return intance;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Singleton04.getIntance());
}).start();
}
}
}
第五种单例懒汉式(线程不安全,同步代码块)
public class Singleton05 {
private static Singleton05 instanct;
private Singleton05(){
}
public static Singleton05 getInstanct(){
if(instanct==null){
synchronized (Singleton05.class){
try {
Thread.sleep(2);
}catch (Exception e){
e.printStackTrace();
}
instanct = new Singleton05();
}
}
return instanct;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Singleton05.getInstanct());
}).start();
}
}
}
测试结果实例化多个对象
第六种单例(双重检查,线程安全)
/**
* 双层检查锁定DCL
*/
public class Singleton06 {
private static volatile Singleton06 instance;//禁止指令重排,JDK1.5以后加上volatile来实现DCL方式是绝对线程安全的
private Singleton06(){
}
public static Singleton06 getInstance(){
if(instance == null){//双重检查
synchronized(Singleton06.class){//锁住整个类
try {
Thread.sleep(2);
}catch (Exception e){
e.printStackTrace();
}
if(instance==null){//双重检查
instance = new Singleton06();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Singleton06.getInstance());
}).start();
}
}
}
第七种单例(静态内部类,线程安全)
/**
* 静态内部类
* 利用了类加载的机制来保证初始化单例时只有一个线程,所以是线程安全的
*/
public class Singleton07 {//外部类
private Singleton05(){
}
private static class InnerClass{//静态内部类
private static final Singleton05 instance = new Singleton05();
}
public static Singleton07 getInance(){
try {
Thread.sleep(2);
}catch (Exception e){
e.printStackTrace();
}
return InnerClass.instance;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Singleton07.getInance());
}).start();
}
}
}
第八种单例(枚举,线程安全)
/**
* 枚举
* 优点:
* JVM会阻止反射强制构造对象,而且可以在对象被反序列化的时候,保证反序列结果返回同一对象,并且是线程安全的
* 缺点:
* 不是延时加载,单例对象在枚举类初始化加载的时候就进行创建了
*/
public enum Singleton08 {
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(Singleton06.INSTANCE);
}).start();
}
}
}