工厂设计模式
工厂设计模式概述在我们平常创建对象的时候,都是通过关键字 new 来实现的,eg: A a = new A() 。
在一些情况下,要创建的对象需要一系列复杂的初始化操作,比如查配置文件、查数据库表、初始化成员对象等,如果把这些逻辑放在构造函数中,会极大影响代码的可读性。不妨定义一个类来专门负责对象的创建,这样的类就是工厂类,这种做法就是工厂模式,在任何需要生成复杂对象的地方,都可以使用工厂模式。
解决的问题
客户端在调用时不想判断来实例化哪一个类或者实例化的过程过于复杂。在工厂模式中,具体的实现类创建过程对客户端是透明的,客户端不决定具体实例化哪一个类,而是交由“工厂”来实例化。
简单工厂
UML图
抽象类或接口:定义了要创建的产品对象的接口
具体实现:实现接口,自定义产品细节。
产品工厂:负责创建产品对象。工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。
代码实现
/** * 抽象产品接口 */public interface Keyboard ...
G1垃圾收集器
G1垃圾收集器
特点: 区域化分代式(Garbage-First)复制算法、并行并发兼具、面向服务端
目标:在延迟可控的情况下获得尽可能高的吞吐量
并行性:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程STW
并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况
分代收集
但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是物理连续的,也不再坚持固定大小和固定数量。
可预测的停顿时间模型
设定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制。
G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。保证了G1收集器在有限的时间内可以获取尽可能高的收集效率
G1 回收器的缺点
用户程序运行过程中,G1无论是为了 ...
Operation System
操作系统的特征并发
并发:两个或多个事件在同一时间间隔内发生,这些事件在宏观上是同时发生的,在微观上是交替发生的, 操作系统的并发性指系统中同时存在着多个运行的程序
并行:两个或多个事件在同一时刻发生
一个单核(CPU)同一时刻只能执行一个程序,因此操作系统会协调多个程序使他们交替进行(这些程序在宏观上是同时发生的,在微观上是交替进行的)
共享
定义:系统中的资源可以供内存中多个并发执行的进程共同使用
互斥共享
计算机中的某个资源在一段时间内只能允许一个进程访问,别的进程没有使用权
临界资源(独占资源):在一段时间内只允许一个进程访问的资源,计算机中大多数物理设备及某些软件中的栈、变量和表格都属于临界资源,它们被要求互斥共享
举个例子:比如QQ和微信视频。同一段时间内摄像头只能分配给其中一个进程
同时共享
计算机中的某个资源在在一段时间内可以同时允许多个进程访问
同时共享通常要求一个请求分为几个时间片段间隔的完成,即交替进行,“分时共享”
这里的同时指在宏观上是同时的,在微观上是交替进行访问的,只是cpu处理速度很快,我们感觉不到,在宏观上感觉是在同时进行
举个例子:比如QQ ...
Spring源码解读
Bean的加载
Bean加载的总体过程
转换对应的beanName
尝试从缓存中加载单例: Spring中的Bean默认是单例的,因此在同一个容器内只会被创建一次,后续再获取Bean就直接从单例缓存中获取。如果尝试从缓存中加载失败,则会再次尝试从SingletonFactories中加载。因为在创建单例Bean的时候存在依赖注入的情况,而在创建依赖Bean的时候为了避免循环依赖,Spring不等Bean创建完成就会将创建Bean的ObejectFactory提早曝光加入到缓存中,当下一个Bean创建时需要依赖上一个Bean则直接使用ObjectFactory去生产Bean
Bean的实例化: 如果从缓存中得到了bean的原始状态,则需要对bean进行实例化缓存中记录的只是最原始的bean状态
原型模式的依赖检测:只有在单例情况下才会尝试解决循环依赖
检测parentBeanFactory:如果缓存中没有直接到父类上进行加载
寻找依赖: 在初始化某一个bean的时候首先会初始化这个bean所对应的依赖
针对不同的scope进行bean的创建: Spring中默认时singleton, ...
动态规划---子序列问题
LeetCode 53 最大子序和给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。输入: [-2,1,-3,4,-1,2,1,-5,4]输出: 6解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
思路分析:
找状态: 数组中的元素
定义dp : 以nums[i]为结尾的最大子数组和为dp[i]
要想得到整个数组的最大子数组和要遍历整个dp数组,找到最大的那个
做选择: dp[i-1] 是否要与nums[i]元素连接,构成更大的子数组
状态转移方程 dp[i] = Math.max(nums[i],dp[i-1] + nums[i])
代码实现
public int maxSubArray(int[] nums) { int len = nums.length; int[]dp = new int[len]; //第一个元素的子数组就是自己 dp[0] = nums[0]; for(int i = 1;i < len;i++) ...
Redis---主从复制
简述 在面对多机集群的环境下,难免要部署多台服务器,那么各个服务器之间是如何保证数据的传输与一致性的呢?
在Redis中,用户可以通过SALAVEOF命令或者slaveof选项,让一个服务器去复制另一个服务器,被复制的服务器称作主服务器,对主服务器复制的服务器称作从服务器。主从服务器双方的数据库将保存相同的数据
旧版复制功能的实现 Redis的复制功能分为同步(sync)和命令传播两个操作:
同步操作用于将从服务器的数据库状态更新至主服务器当前的数据库状态
命令传播则用于在主服务器的数据库状态被修改时,导致主从服务器的数据库状态出现不一致时,让主服务器的数据库重新回到一致状态
同步 从服务器复制主服务器时,从服务器首先需要执行同步操作。而这个同步操作需要通过向主服务器发送sync命令来完成
从服务器向主服务器发送sync命令
主服务器收到sync命令后会执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有命令
BGSAVE命令执行完毕后,会将RDB文件发送给从服务器
主服务器将缓存里所有的写命令都发送 ...
动态规划---背包问题
背包问题
问题陈述
给你一个可装载重量为W的背包和N个物品,每个物品有重量和价值两个属性。其中第i个物品的重量为wt[i],价值为val[i],现在让你用这个背包装物品,最多能装的价值是多少?
N = 3, W = 4wt = [2, 1, 3]val = [4, 2, 3]
问题解析
明确状态: 背包容量 和 物品数量
确定选择: 是否将物品装入背包
确定子问题: 放i-1个物品的最大价值,如果此时背包没装满,可以装最后一件物品,那么最大价值要再加上最后一件物品的价值,如果装满了那最大价值就是前i-1个物品的最大价值
定义状态转移方程: dp[m] [n] = Math.max(dp[m-i] [n],dp[m-1] [n-wt[i-1]] + val[i-1])。该转移方程的定义就是,前m件商品放在容量为n的背包中的最大价值是多少
明确初始条件,如果背包容量为0说明放满了,此时最大价值为0,如果没有物品了,自然最大价值也为0
处理边界情况:根据状态转移方程我们可以看出 n-wt[i-1] 必须要 >= 0,因为这样才可以装最后一件物品,如果小于0说明无 ...
Redis持久化机制
为什么要做持久化 因为Redis是内存数据库,数据是存储在内存中的,一旦服务器宕机,数据就会丢失,因此在对内存中的数据做持久化操作。在Redis中,持久化操作主要有两种方式,一种是RDB一种是AOF。
RDB 持久化 RDB 持久化既可以手动执行,又可以在服务器配置选项中定期执行,RDB持久化机制是通过保存某个时间点的数据库状态到一个RDB文件中实现的。
RDB文件的创建与载入 有两个Redis命令可以用于生成RDB文件,一个是SAVE一个是BGSAVE
SAVE
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何请求
BGSAVE
BGSAVE命令会fork一个子进程,然后由子进程负责创建RDB文件,父进程继续处理命令请求。
BGSAVE命令执行期间,客户端发送的SAVE命令会被服务器拒绝,目的是为了避免父进程和子进程同时执行持久化,产生竞争条件。同时客户端的BGSAVE命令也会被禁止
RDB文件的载入工作是在服务器启动时自动执行的,只要检测到由RDB文件的存在,就会自动载入 ...
Redis键的过期删除策略
前情提要 Redis服务器的所有数据库都保存在一个db数组中,而数据库的数量则是由一个dbnum的属性进行保存。客户端通过修改目标数据库的指针,让它指向db数组中的不同元素,从而达到切换不同数据库的目的。
读写键空间时的维护操作 Redis中的键值主要是存储在一个dict的字典中,这个字典又叫做键空间,键保存的是我们设置的键字符串对象,值保存的是Redis所允许的值类型对象。
在读取一个键之后,服务器会根据键是否未更新服务器的键空间的命中次数与不命中次数
读取一个键后,服务器会更新键的LRU(最后一次使用)时间,LRU可用来计算键的闲置时间
如果服务器在读取一个键时发现键已经过期,那么服务器会先删除这个过期键然后在执行其他操作。
如果客户端使用WATCH命令监视某个键,那么服务器在对被监视的键进行修改之后,会将这个键标记为”脏”,从而让事务程序注意到这个键已经被修改过
服务器每次修改一个键后,都会对脏键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作
如果服务器卡起了数据库通知功能,那么在对键进行修改后,服务器会发送通知。
设置键的过期时间 通过E ...
动态规划
适用动态规划的问题
计数问题
有多少种方式走到右下角
有多少种方法选出K个数使和为sum
求最大值和最小值
从左上角到右下角路径的最大数字和
最长上升子序列长度
求存在性
取石子问题,先手是否必胜
能不能选出K个数使得和为sum
解题框架
确定状态:子问题和最后一步。找到最后一步将一个大问题转化为一个小问题
根据子问题的转化过程,确定状态转移方程。定义状态转移方程,一般用数组表示,有几个状态就对应着几维数组
确定初始条件和边界情况。初始条件一般是我们转移方程求不出来的量,边界情况一般要考虑数组索引是否小于是否下标越界等。
弄清楚计算顺序。一般是从小到大,从上左到右的顺序
例题LeetCode 70 爬楼梯假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。 输入: 2输出: 2解释: 有两种方法可以爬到楼顶。1. 1 阶 + 1 阶2. 2 阶
思路分析
通过读题,我们发现问题是”有多少种不同的方法可以爬到楼顶”,很显然这是动态规划中的第一类问题计 ...