1.Redis分布式锁如何实现的
分布式锁一般需要从几个方面去考虑,正确性和效率,分布式锁一般需要一个全局的key,然后持有锁的用完必须进行释放锁,如果忘记释放,必须要有过期时间。因为redis命令执行的时候是以单线程进行执行的,符合天然的原子性条件之一,没有冲突,可以考虑用setnx命令去设置锁,值存在的时候才有效,并且在高版本的时候还可以设置过期时间,解锁的时候需要加锁的值进行匹配,防止被其他人进行解锁。除了用sentx命令之外,还可以用lua脚本多个命令批量执行。初次之外,在一些业务场景下面,常常用数据库的唯一键来做幂等处理。
2.分布式锁还有哪些实现方案
用的多的有数据库的唯一索引、redis的单线程实行的原理和zomkeeper等。
3.线程池是用来处理啥的、使用的业务场景、解决什么业务问题
线程池存在是为加快响应处理业务,减少了线程创建的等待时间,线程池主要是对工作的线程进行统一管理,从内存、调度和GC的角度去考虑,线程池在业务上应用很广,比如MySQL连接池,TomCat的连接池等。
4.让你实现一个RPC框架,应该要考虑哪些点
协议、压缩方式、端口号、序列化。
5.阻塞IO和非阻塞IO有什么区别
文件从磁盘或者网卡传输到用户缓冲区的时候,会先将数据拷贝到内核区,在从内核去拷贝到用户区,阻塞时只从数据从外部拷贝到内核区的时候,用户进程时阻塞还是轮询,轮询时非常消耗cpu资源,阻塞的话,应用进程进行休眠。
6.如何实现多路复用IO
要实现IO多路复用,一般是通过一个slect去监听多个文件描述符,当有读写时间来的时候,在进行处理读写事件,这就实现了IO多路复用。
7.select、epoll原理
select是通过监听文件描述符,具体流程是,将监听的文件描述符从用户区拷贝到内核区,通过内核去遍历文件哪些文件描述符已经就绪,然后将就绪的拷贝到用户区,在进行遍历,将满足条件的文件描述符分别进行读或者写,select监听的文件描述符是有上限的,上限为1024个,select基本和poll相似。epoll是解决了文件描述符集合在用户态和内核态之间拷贝和遍历的开销,epoll是由事件进行驱动的,有epoll_create,epoll_ctl,epoll_wait,epoll_ctl是将新监听的文件描述符加入到内核区域维护的一个B+树中,当有就绪的时候,直接通过epoll_wait就可以返回就绪的文件描述符。
9.go语言实现堆排序。
// 5个核心的方法
type Que []int
func (que Que)Len{return len(que)}
func (que Que)Less(i , j int)bool{return que[i] < que[j]}
func (que Que)Swap(i , j int){que[i] , que[j] = que[j] , que[i]}
func (que *Que)Push(x interface{}){*que = append(*que , x.([]int)}
func (que *Que)Pop interface{}{
old := que
n := len(old)
x := old[n - 1]
*que = old[:n-1]
return n
}
10.算法
-
反转链表
头插法
-
最长回文子串
遍历
-
子集
dfs
-
n个骰子的点数和为k的概率
暴力解法:dfs
动态规划,上一层的状态与下一层的状态之间的联系。
func main() {
n := 2
k := 11
dp := make([][]float64, n+1)
var dfs func(n, k int) float64
dfs = func(n, k int) float64 {
if n < 0 || k < 0 {
return 0
}
if dp[n][k] != 0 {
return dp[n][k]
}
for i := 1; i <= 6; i++ {
if k >= i {
dp[n][k] = dp[n][k] + dfs(n-1, k-i)/6
}
}
return dp[n][k]
}
for i := 0; i <= n; i++ {
dp[i] = make([]float64, k+1)
}
dp[0][0] = 1
dfs(n, k)
fmt.Println(dp[n][k])
fmt.Println(float64(1) / 18)
}