目录
1 单例模式
1.1 饿汉式
私有构造函数,禁止外部访问。
使用static和final做到类加载到内存后,就实例化一个单例,而且是唯一的,JVM保证线程安全。简单实用。
唯一缺点:不管用到与否,类装载时就完成实例化,占用内存。
1.1.1 写法1
package com.DesignPattern.singleton;
/*
饿汉式
类加载到内存后,就实例化一个单例,JVM保证线程安全
简单实用
唯一缺点:不管用到与否,类装载时就完成实例化
*/
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();//类加载时(static)就进行对象实例化(final),是唯一的
private Mgr01(){};//私有构造函数,禁止外部访问
// 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的。被static关键字修饰的变量、方法属于类变量、类方法,
// 可以通过【类名.变量名】、【类名.方法名】直接引用,而不需要派生一个类实例出来。
public static Mgr01 getInstance(){return INSTANCE;}
public void m(){System.out.println("m");}//主方法
// 测试一把,是否单例
public static void main(String args[]){
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m2);
}
}
1.1.2 写法2
用static语句块来做初始化
package com.DesignPattern.singleton;
/*
饿汉式
*/
public class Mgr02 {
private static final Mgr02 INSTANCE;
static {
INSTANCE = new Mgr02();
}
private Mgr02() {
}//私有构造函数,禁止外部访问
public static Mgr02 getInstance() {
return INSTANCE;
}
public void m() {
System.out.println("m");
}//主方法
// 测试一把,是否单例
public static void main(String args[]) {
Mgr02 m1 = Mgr02.getInstance();
Mgr02 m2 = Mgr02.getInstance();
System.out.println(m1 == m2);
}
}
1.2 懒汉式
在使用时再进行类的实例化。
1.2.1 写法1
使用if语句避免多次实例化,缺点是多线程访问时无法保证唯一性。
package com.DesignPattern.singleton;
/*
懒汉式
非线程安全
*/
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03() {
}//私有构造函数,禁止外部访问
public static Mgr03 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}//主方法
// 测试一把,是否单例
public static void main(String args[]) {
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr03.getInstance().hashCode());
}
}).start();
}
}
}
1.2.2 写法2
加锁保证线程安全,缺点是给整个获取实例的语句块加锁导致效率降低。
package com.DesignPattern.singleton;
/*
懒汉式
加锁保证线程安全
但整个语句块加锁导致效率降低
*/
public class Mgr04 {
private static Mgr04 INSTANCE;
private Mgr04() {
}//私有构造函数,禁止外部访问
public static synchronized Mgr04 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr04();
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}//主方法
// 测试一把,是否单例
public static void main(String args[]) {
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr04.getInstance().hashCode());
}
}).start();
}
}
}
1.2.3 写法3
试图减小同步代码块以提高效率,下面这种写法不可行。不可行的原因是,if语句执行过的线程,都会进入执行同步语句块。if语句判断是否存在实例没有加锁,给执行语句块加锁没有意义。
所以这种写法是线程不安全的,加锁没有意义。
package com.DesignPattern.singleton;
/*
懒汉式
试图减小同步代码块以提高效率,这种写法不可行
*/
public class Mgr05 {
private static Mgr05 INSTANCE;
private Mgr05() {
}//私有构造函数,禁止外部访问
public static Mgr05 getInstance() {
if (INSTANCE == null) {
//试图通过减小同步代码块的方式提高效率,这种写法不可行
synchronized (Mgr05.class){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr05();
}
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}//主方法
// 测试一把,是否单例
public static void main(String args[]) {
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr05.getInstance().hashCode());
}
}).start();
}
}
}
1.2.4 写法4
双重检查单例写法,懒汉式。是上一种写法的改良,相比写法2提升了效率,因为不是每个线程都需要加锁了,提高了并发。
在类被一个线程实例化之后,尚未进入第一层if的线程不会执行同步块语句;已经进入同步块之内的线程,执行完第二层if就会归还锁。
注意:为了多线程创建和访问的安全性,需要加volatile关键字。
package com.DesignPattern.singleton;
/*
懒汉式
同步代码块+双判断
线程安全且高效
*/
public class Mgr06 {
private static volatile Mgr06 INSTANCE;
private Mgr06() {
}//私有构造函数,禁止外部访问
public static Mgr06 getInstance() {
//第一层用于提高效率,一旦类已被实例化,就不再往下执行
if (INSTANCE == null) {
synchronized (Mgr06.class){
//第二层用于保证单例,防止进入第一层if的线程,获取锁之后再次实例化类
if(INSTANCE==null){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}//主方法
// 测试一把,是否单例
public static void main(String args[]) {
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr06.getInstance().hashCode());
}
}).start();
}
}
}
1.3 静态内部类
1.3.1 写法1
在类的内部定义静态内部类,在该静态内部类中定义初始化的外部类的静态实例。
在获取外部类的实例的时候,返回静态内部类定义的经过初始化的外部类的静态实例。
由于加载外部类时不会加载内部类,这样可以实现懒加载。
JVM保证单例,因为JVM在加载类的时候只会加载一次。
package com.DesignPattern.singleton;
/*
静态内部类方式
JVM保证单例
由于加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Mgr07 {
private Mgr07(){};//私有构造函数,禁止外部访问
private static class Mgr07Holder{
private static final Mgr07 INSTANCE = new Mgr07();
}
public static Mgr07 getInstance(){
return Mgr07Holder.INSTANCE;
}
public void m(){System.out.println("m");}//主方法
// 测试一把,是否单例
public static void main(String args[]){
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Mgr06.getInstance().hashCode());
}
}).start();
}
}
}
1.4 枚举实现
1.4.1 写法1
既能保证线程安全,也可以防止反序列化。(没有深究,这种写法没太理解)
package com.DesignPattern.singleton;
public enum Mgr08 {
INSTANCE;
public void m(){}
// 测试一把,是否单例
public static void main(String args[]){
for(int i=0;i<100;i++){
new Thread(() -> System.out.println(Mgr08.INSTANCE.hashCode())).start();
}
}
}