• 创建型模式:
– 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
• 结构型模式:
– 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模 式。
• 行为型模式:
– 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模 式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
就是由软件界大佬GOF总结出的一些java写代码的时候的固定套路。不要背,去用,分析语句好处。
1.单例模式
一个类只有一个实例对象,提供一个public方法获取这个对象。
应用场景:
创建一个对象需要比较多的资源时,比如读取配置文件,产生其他依赖对象,就用单例模式,应用启动的时候创建一次,永久保存在内存中。
1)饿汉式
package com.bjsxt.singleton;
import java.io.Serializable;
/**
* 饿汉模式
*/
public class SingletonDemo1 implements Serializable {
// 没有延迟加载 线程安全
private static SingletonDemo1 instance = new SingletonDemo1();
// 构造器私有 一个类只有一个对象
private SingletonDemo1(){}
public static SingletonDemo1 getInstance(){
return instance;
}
}
饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问 题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省略synchronized关键字。
问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
2)懒汉式
package com.bjsxt.singleton;
public class SingletonDemo2 {
private static SingletonDemo2 instance;
private SingletonDemo2(){
}
// 线c安全 延迟加载 调用效率低
public synchronized static SingletonDemo2 getInstance() {
if(instance==null){
instance=new SingletonDemo2();
}
return instance;
}
}
lazy load! 延迟加载, 懒加载! 真正用的时候才加载!
问题: – 资源利用率高了。但是,每次调用getInstance()方法都要同步,并发 效率较低。
3)双重检测锁实现
4)静态内部类实现方式(也是一种懒加载方式)
package com.bjsxt.singleton;
public class SingletonDemo3 {
private static class SingletonDemo3Inline{
private static SingletonDemo3 instance = new SingletonDemo3();
}
private SingletonDemo3(){}
public static SingletonDemo3 getInstance(){
return SingletonDemo3Inline.instance;
}
}
外部类没有static属性,则不会像饿汉式那样立即加载对象。
– 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性.
– 兼备了并发高效调用和延迟加载的优势
• 问题:
反射设置access为true可以破坏一个类只有一个对象(不包含枚举式)!反序列化(不包含枚举式)将对象写入文件之后读取文件到对象会生成新的对象!也违背了一个类只有一个对象!
package com.bjsxt.singleton;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
public class Client2 {
public static void main(String[] args) throws Exception {
// 反射破坏饿汉模式 一个类不只有一个对象
Class<SingletonDemo1> singletonDemo1Class= (Class<SingletonDemo1>) Class.forName("com.bjsxt.singleton.SingletonDemo1");
Constructor<SingletonDemo1> constructor = singletonDemo1Class.getDeclaredConstructor(null);
constructor.setAccessible(true);
SingletonDemo1 s1=constructor.newInstance();
SingletonDemo1 s2=constructor.newInstance();
System.out.println(s1);
System.out.println(s2);
// 反射破坏懒汉模式 一个类不只有一个对象
Class<SingletonDemo2> singletonDemo2Class2= (Class<SingletonDemo2>) Class.forName("com.bjsxt.singleton.SingletonDemo2");
Constructor<SingletonDemo2> constructor2 = singletonDemo2Class2.getDeclaredConstructor(null);
constructor2.setAccessible(true);
SingletonDemo2 s3=constructor2.newInstance();
SingletonDemo2 s4=constructor2.newInstance();
System.out.println(s3);
System.out.println(s4);
// 反射破坏静态内部类模式
Class<SingletonDemo3> singletonDemo3Class3= (Class<SingletonDemo3>) Class.forName("com.bjsxt.singleton.SingletonDemo3");
Constructor<SingletonDemo3> constructor3 = singletonDemo3Class3.getDeclaredConstructor(null);
constructor3.setAccessible(true);
SingletonDemo3 s5=constructor3.newInstance();
SingletonDemo3 s6=constructor3.newInstance();
System.out.println(s5);
System.out.println(s6);
// 反射破坏枚举方式 无法破坏!
/*Class<SingletonDemo4> singletonDemo4Class= (Class<SingletonDemo4>) Class.forName("com.bjsxt.singleton.SingletonDemo4");
Constructor<SingletonDemo4> constructor4 = singletonDemo4Class.getDeclaredConstructor(null);
constructor4.setAccessible(true);
SingletonDemo4 s7=constructor4.newInstance();
SingletonDemo4 s8=constructor4.newInstance();
System.out.println(s7);
System.out.println(s8);*/
// 反序列化破坏单例模式 一个类不只有一个对象
FileOutputStream fos=new FileOutputStream("d:/a.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(s1);
oos.close();
fos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletonDemo1 s10 = (SingletonDemo1) ois.readObject();
System.out.println(s1);
System.out.println(s10);
}
}
运行结果:
解决办法:
5)枚举
package com.bjsxt.singleton;
public enum SingletonDemo4 {
INSTANCE;
public void operateInstance(){
}
}
优点:
– 实现简单
– 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
• 缺点:
– 无延迟加载
2 常见的五种单例模式在多线程环境下的效率测试
CountDownLatch
– 同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一 个或多个线程一直等待。
countDown() 当前线程调此方法,则计数减一(建议放在 finally里执行)
await(), 调用此方法会一直阻塞当前线程,直到计时器的值为0
package com.bjsxt.singleton;
import java.util.concurrent.CountDownLatch;
public class Client3 {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<3;i++){
test1();
test2();
test3();
test4();
System.out.println("-------");
}
}
public static void test1(){
int threadNum=10;
CountDownLatch countDownLatch=new CountDownLatch(threadNum);
long start=System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
new Thread(()->{
for(int j=0;j<1000000;j++){
SingletonDemo1 instance = SingletonDemo1.getInstance();
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("单例模式:饿汉模式模式"+(end -start)+" ms");
}
public static void test2(){
int threadNum=10;
CountDownLatch countDownLatch=new CountDownLatch(threadNum);
long start=System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
new Thread(()->{
for(int j=0;j<1000000;j++){
SingletonDemo2 instance = SingletonDemo2.getInstance();
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("单例模式:懒汉模式"+(end -start)+" ms");
}
public static void test3(){
int threadNum=10;
CountDownLatch countDownLatch=new CountDownLatch(threadNum);
long start=System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
new Thread(()->{
for(int j=0;j<1000000;j++){
SingletonDemo3 instance = SingletonDemo3.getInstance();
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("单例模式:静态内部类模式"+(end -start)+" ms");
}
public static void test4(){
int threadNum=10;
CountDownLatch countDownLatch=new CountDownLatch(threadNum);
long start=System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
new Thread(()->{
for(int j=0;j<1000000;j++){
SingletonDemo4 instance = SingletonDemo4.INSTANCE;
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("单例模式:枚举"+(end -start)+" ms");
}
}
运行结果
使用uml看类图
3 总结: