标题:《打卡面经》设计模式与jdk源码阅读
个人浅见,逐步学习,思进取
关键词:策略模式、单例模式、代理模式、观察者模式、线程池、锁集合
一、设计模式
1、策略模式
基础代码
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd()); //策略方法
System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); //通用调用
context = new Context(new OperationSubtract()); //策略方法
System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); //通用调用
}
}
使用场景
1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
优缺点
优 :1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺 :1、策略类会增多。 2、所有策略类都需要对外暴露。
参考文档 菜鸟教程 https://www.runoob.com/design-pattern/strategy-pattern.html
2、单例模式
懒汉式-线程不安全
Lazy初始化(使用到才创建)
核心代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式-线程安全
缺点:获取时必须抢锁,后续影响性能
核心代码
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式
线程安全,容易产生垃圾对象(提前创建不一定会被使用)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
Doudle Check+volatile(双重校验锁)
懒加载,线程安全且保证性能
核心代码-volatile和两重if
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类
达到和Double Check一样的效果,利用类的加载特性-静态内部类只有在挂载的类被显式调用方法时才会被显式转载从而初始化
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
枚举类的特性,在JVM层面保证线程安全-枚举类的构造方法始终只会被执行一次
核心代码
enum EnumSingleton {
INSTANCE;
private Integer singletonInt;
EnumSingleton() {
singletonInt = new Integer(11);
System.out.println("init");
}
public Integer getInstnce(){
return singletonInt;
}
}
@Test
public void t1(){
Singleton.getInstance();
System.out.println(EnumSingleton.INSTANCE.getInstnce());
}
3、代理模式
简介:在做某件事情上,做些额外动作
重点Spring的Aop(动态代理)
Spring的动态代理有两种实现方式JDK或CGLIB
1.执行后置处理器的方法-Aop的实现就在这个后置处理器中
2.选择具体的动态代理实现-JDK:基于实现目标类接口 CGLIB:基于继承目标类
参考文档:https://blog.csdn.net/weixin_41653813/article/details/123931987
4、观察者模式
对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作–有点消息队列的意思。
核心代码
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject(); //主题
new HexaObserver(subject); //观察者监听主题
new OctalObserver(subject);//观察者监听主题
new BinaryObserver(subject);//观察者监听主题
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
参考文档:https://www.runoob.com/design-pattern/observer-pattern.html
二、Java基础
1、线程池
Executors-线程池工具类,不推荐使用
Executors.newScheduledThreadPool(2);;//args-int 线程无上限,线程可能会很多,导致OOM
Executors.newCachedThreadPool();//0-int 线程无上限,线程可能会很多,导致OOM
Executors.newFixedThreadPool(5); //线程数量固定,任务队列容易堆积,导致OOM
Executors.newSingleThreadExecutor();//线程数量固定,任务队列容易堆积,导致OOM
ThreadPoolExecutor-线程池对象-推荐使用
全参构造方法(核心线程数,最大线程数,保活时间,保活时间单位,任务队列,线程创建工厂,任务拒绝策略)
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//保活时间
TimeUnit unit,//保活时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程创建工厂
RejectedExecutionHandler handler//任务拒绝策略
) {
四种任务拒绝策略
1 ThreadPoolExecutor.AbortPolicy 默认拒绝策略,拒绝任务并抛出任务
2 ThreadPoolExecutor.CallerRunsPolicy 使用调用线程直接运行任务
3 ThreadPoolExecutor.DiscardPolicy 直接拒绝任务,不抛出错误
4 ThreadPoolExecutor.DiscardOldestPolicy 触发拒绝策略,只要还有任务新增,一直会丢弃阻塞队列的最老的任务,并将新的任务加入
入口源码
java.util.concurrent.ThreadPoolExecutor#execute - 线程池提交任务方法
添加任务
java.util.concurrent.ThreadPoolExecutor#addWorker - 线程池对象的添加worker方法
执行任务
java.util.concurrent.ThreadPoolExecutor.Worker#run - worker的run方法
其中还有一些核心方法如:getTask(),其实也差不多
2、Syn与Lock
Sync内部维护了一个monitor对象,用于管理锁的开销。其内部有锁池(抢锁失败的线程)、等待池(调用锁wait方法的线程)、锁持有线程、重入次数、竞争逻辑。
Sync锁粗化
1.偏向锁:未出现锁竞争,始终一个线程,在对象头中记录偏向锁信息(线程id);
2.轻量锁:出现了锁竞争,会自旋(CAS)竞争锁在自旋一定次数后升级至重量锁;
3.重量级锁:将其阻塞,等待锁的释放;
Lock基于AQS的封装,AQS的核心在于一个用户态(state)在对其CAS操作的封装。AQS默认非公平,在获取锁的时候会自旋一次,然后将当前线程通过LockSupport的方式进行阻塞,然后封装为NODE节点,放入阻塞链表的末尾。当锁释放后,从阻塞链表的头部取出一个节点,将线程唤醒,继续抢夺锁资源。由于期间可能出现非阻塞链表的线程参与锁资源的争夺,对排队的线程不公平,所以为非公平锁。
熟悉的线程工具类CountDownLatch、CyclicBarrier等是基于AQS实现。
3、 集合源码
ArrayList
底层采用数组实现
默认长度
扩容倍数与数组拷贝
删除位移
fail-fast机制(因为非线程安全而存在)-在继承的抽象方法AbstractList中记录有modCount数值,该数值记录数组被修改与删除的次数,在获取数组迭代器中会拷贝modCount,在每次迭代中会比较拷贝的modCount与当前modCount是否一致,如果不一致那就会触发fail-fast。
fail-fast异常:抛出异常
fail-fast机制:add方法记录modCount
HashMap核心方法-put
扩容核心方法-resize()
ConcurrentHashMap控制并发安全-锁