秋招复习:Java后台开发 - 0304

粘包现象怎么处理

TCP有粘包现象,而UDP不会出现粘包。

TCP: 是面向连接的,面向流的。TCP的收发两端都要有成对的Socket,因此,发送端为了将更多有效的包发送出去,采用了合并优化算法(Nagle算法),将多次、间隔时间短、数据量小的数据合并为一个大的数据块,进行封包处理。 这样的包对于接收端来说,就没办法分辨,所以需要一些特殊的拆包机制。
UDP: 是无连接的,面向消息的提供高效率服务。不会使用合并优化算法。 UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。

我们连续发送三个数据包,大小分别是1k,2k ,4k,这三个数据包,都已经到达了接收端的网络堆栈中,如果使用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有三次接收动作,才能够把所有的数据包接收完.而使用TCP协议,我们只要把接收的缓冲区大小设置在7k以上,我们就能够一次把所有的数据包接收下来,只需要有一次接收动作。

如何处理粘包

  • 提前通知接收端要传送的包的长度
    粘包问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
    不建议使用,因为程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这样会放大网络延迟带来的性能损耗

  • 加分割标识符
    eg. /n
    {数据段01}+标识符+{数据段02}+标识符
    发送端和接收端约定好一个标识符来区分不同的数据包,如果接收到了这么一个分隔符,就表示一个完整的包接收完毕。
    也不建议使用,因为要发送的数据很多,数据的内容格式也有很多,可能会出现标识符不唯一的情况

  • 自定义包头(建议使用)
    在开始传输数据时,在包头拼上自定义的一些信息,比如前4个字节表示包的长度,5-8个字节表示传输的类型(Type:做一些业务区分),后面为实际的数据包。
    参考这片文章

LInux常用命令,如何查看磁盘容量,cpu用量,各端口号

  • 查看端口是否被占用
    netstat -an|grep 端口号
  • 根据port端口号显示具体进程号(pid)
    lsof -i:端口号
  • top:动态查看进程(相对于ps) 以及进程所占CPU、MEM等
    P M N 按下分别会按照CPU 、MEM内存占用,以及进程号的大小倒序显示。
  • uptime : 显示系统已经开机运行多久,以及1,5,15分钟的平均负载:
  • 查看CPU信息 #cat /proc/cpuinfo
  • 查看内存信息 #cat /proc/meminfo
  • 查看硬盘分区情况: #df -lh
  • du -sh [目录名]:返回该目录的大小
  • du -s /* | sort -nr :查看那个目录占用空间大,然后一层层排查

快速判断一个数是不是2的n次

将2的幂次方写成二进制形式后,很容易就会发现有一个特点:二进制中只有一个1,并且1后面跟了n个0; 因此问题可以转化为判断1后面是否跟了n个0就可以了。

最快速的方法: (number & number - 1) == 0

求一个数n的二进制中1的个数。

非常巧妙地利用了以上性质,n=n&(n-1) 能移除掉n的二进制中最右边的1的性质,循环移除,直到将1全部移除,这种方法将问题的复杂度降低到只和1的个数有关系。

int Func3(int data)
{   //利用了data&(data-1)每次都能移除最右边的1,移除了多少个1,就是包含了几个1
	int count = 0;
	while (data)
	{
		data = data & (data-1);
		count++;
	}
	return count;
}

扩展问题二:A和B的二进制中有多少位不相同。

这个问题可以分为两步:
(1)将A和B异或得到C,即C=A^B,
(2)计算C的二进制中有多少个1。

n!(阶乘)末尾有多少个0

要求得末尾为0的个数,就要先分析一下末尾的0是怎么产生的,末尾的0肯定是由2和5产生的, 一对2,5相乘产生10,这样末尾就有一个0了。按照常识,一个数中5的个数肯定是要少于2的个数,那么我们只需要求5的个数就好了。这么整个问题就变为在N!中求5的个数就行了。

lines=[]
count=int(input())  # 输入N的个数
for i in range(count):
    lines.append(int(input()))

def ZerosCount(N):
    """
     Count the Numner of zero in the end of the N!
    """
    num=0
    for k in range(1,N+1):
        j=k
        while(j%5==0):
              num+=1
              j=j/5
    return num
print(list(map(ZerosCount,lines)))

HashMap碰撞的概率

假设这个Hash函数是非常好的。它的Hash值均匀地分布在值域上。
在这种情况下,对于一个输入集合生成的Hash值是非常像生成一个随机数集合。我们的问题转化为如下:
给K个随机值,非负而且小于N,他们中至少有个相等的概率是多少?,我们用1减去对立问题的结果就得到原问题的结果:

(N-1/N) (N-2/N)…*(N-(k-1)/N)

如果N远大于K。所以我们可以更加化简为:K^2/2N

ReentrantLock和synchronized的区别

ReentrantLocksynchronized
乐观锁悲观锁
自旋一段时间再阻塞直接阻塞。。。
可重入锁可重入锁
API层面的互斥锁java语言的关键字,是原生语法层面的互斥,需要jvm实现
(非)公平锁非公平锁
等待可中断不可中断,如果Thread1不释放,Thread2将一直等待,不能被中断
可以同时绑定多个Condition对象锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件。但如果要和多于一个的条件关联的时候,就不得不额外添加一个锁。

jvm 里 new 对象时,堆会不会发生抢占?那怎么设计jvm的堆的线程安全?

会,假设JVM虚拟机上,每一次new 对象时,指针就会向右移动一个对象size的距离,一个线程正在给A对象分配内存,指针还没有来的及修改,另一个为B对象分配内存的线程,引用这之前的指针指向,这就发生了抢占,也被称为指针碰撞 --> CAS解决
Thread Local Allocation Buffer,线程本地分配缓存:JVM在内存新生代Eden Space中开辟了一小块线程私有的区域TLAB(Thread-local allocation buffer)。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有,所以没有锁开销。
解决:每个线程先在自己的LTAB里面分配对象,如果对象太大或者说空间已经被用完,就在共用的那一块堆中通过CAS分配内存。

用uuid做主键可以吗?为什么?

太长,非递增导致B+tree频繁分裂等等。。。

为什么 object的方法 notify 和wait方法必须在synchronized里使用?

  • 调用wait()就是释放锁,释放锁的前提是必须要先获得锁,先获得锁才能释放锁。
  • notify(),notifyAll()是将锁交给含有wait()方法的线程,让其继续执行下去,如果自身没有锁,怎么叫把锁交给其他线程呢;(本质是让处于入口队列的线程竞争锁)

首先,要明白,每个对象都可以被认为是一个"监视器monitor",这个监视器由三部分组成(一个独占锁,一个入口队列,一个等待队列)。
对于对象的同步方法而言,只有拥有这个对象的独占锁才能调用这个同步方法。如果这个独占锁被其他线程占用,那么另外一个调用该同步方法的线程就会处于阻塞状态,此线程进入入口队列。
若一个拥有该独占锁的线程调用该对象同步方法的wait()方法,则该线程会释放独占锁,并加入对象的等待队列;(为什么使用wait()?希望某个变量被设置之后再执行,notify()通知变量已经被设置。)
某个线程调用notify(),notifyAll()方法是将等待队列的线程转移到入口队列,然后让他们竞争锁,所以这个调用线程本身必须拥有锁。

金条分为相连的7段,给一个人发7天工资,怎么发切断次数最少

最少应该切两次。分别把金条切成七分之一、七分之二、七分之四,共计三段。第一天,给雇员七分之一那段。第二天给七分之二那段,要回七分之一那段。第三天给七分之一那段。第四天给七分之四那段,要回七分之一和七分之二那两段。第五天给他七分之一那段。第六天给他七分之二那段,要回七分之一那段。第七天给他七分之一那段

redis 有序集合(ZSET)的底层是什么,跳表的特点有哪些,具体实现是什么。比如插入过程说一下。

redis 有序集合的底层是什么:跳表

跳表

跳表的插入查找效率为O(logN),
空间复杂度是 O(n)
空间换时间
-
推荐阅读

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist; //跳表

跳表中的每个节点用数据结构 zskiplistNode 表示,head 和 tail 分别指向最底层链表的头尾节点。length 表示当前跳表最底层链表有多少个节点,level 记录当前跳表最高索引层数。
zskiplistNode 结构如下:

typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span;
    } level[];
} zskiplistNode;

redis 中的有序集合结构

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

redis 中的有序集合是由我们之前介绍过的字典加上跳表实现的,字典中保存的数据和分数 score 的映射关系,每次插入数据会从字典中查询,如果已经存在了,就不再插入,有序集合中是不允许重复数据。

查询/插入过程:

  • 从最高索引层开始遍历,根据 score 找到它的前驱节点,用 update 数组进行保存
  • 每一层得进行节点的插入,并计算更新 span 值
  • 修改 backward 指针与 tail 指针

删除节点也是类似的,首先需要根据 score 值找到目标节点,然后断开前后节点的连接,完成节点删除。

redis的IO模型,从select讲到了poll,epoll,详细将epoll。为什么epoll只拷贝一次,而另外两种需要拷贝多次。具体是通过什么机制实现的。回调,哪里体现到了回调。

Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现的。
I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的操作。
推荐阅读

TCP 拥塞控制,流量控制说一下

(一)慢开始算法
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。
慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。
(二)拥塞避免算法:
一开始拥塞窗口成倍增大,到一定程度,拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。
(三)快重传算法:略
(四)快恢复算法:
快重传配合使用的还有快恢复算法
当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半(为了预防网络发生拥塞)。但是接下去并不执行慢开始算法考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。 所以此时不执行慢开始算法,而是将cwnd设置为ssthresh减半后的值,然后执行拥塞避免算法,使cwnd缓慢增大。

hashmap和hashtable 有什么区别?

hashmaphashtable
不同步同步
继承AbstractMap类继承自Dictionary类
允许nullkey/value都不能为null
扩容2的整数次幂2n+1

简单介绍一下java的生命周期有哪些阶段吗?

加载,验证,准备,连接,初始化,销毁

现在数据库执行过长,如何对它进行优化?

1、查看sql是否涉及多表的联表或者子查询,如果有,看是否能进行业务拆分,相关字段冗余或者合并成临时表
2、涉及连表的查询,是否能进行分表查询,单表查询之后的结果进行字段整合
3、考虑对相对应的查询条件做索引。加快查询速度
4、针对数量大的表进行历史表分离(如交易流水表)
5、数据库主从分离,读写分离,降低读写针对同一表同时的压力,至于主从同步,mysql有自带的binlog实现 主从同步
6、explain分析sql语句,查看执行计划,分析索引是否用上,分析扫描行数等等
7、查看mysql执行日志,看看是否有其他方面的问题

说一下数据库有哪些索引类型,有什么优缺点?

普通索引、唯一索引、聚集索引、主键索引、全文索引

使用索引的优点:

  1. 提高数据的搜索速度
  2. 加快表与表之间的连接速度
  3. 在信息检索过程中,若使用分组及排序子句进行时,通过建立索引能有效的减少检索过程中所需的分组及排序时间,提高检索效率。

使用索引的缺点:

  1. 在我们建立数据库的时候,需要花费的时间去建立和维护索引,而且随着数据量的增加,需要维护它的时间也会增加。
  2. 在创建索引的时候会占用存储空间。
  3. 在我们需要修改表中的数据时,索引还需要进行动态的维护,所以对数据库的维护带来了一定的麻烦。

唯一索引:在创建唯一索引时要不能给具有相同的索引值。
主键索引:在我们给一个字段设置主键的时候,它就会自动创建主键索引,用来确保每一个值都是唯一的。
聚集索引:我们在表中添加数据的顺序,与我们创建的索引键值相同,而且一个表中只能有一个聚集索引。
普通索引:它的结构主要以B+树和哈希索引为主,主要是对数据表中的数据进行精确查找。
全文索引:它的作用是搜索数据表中的字段是不是包含我们搜索的关键字,就像搜索引擎中的模糊查询。

聚集(clustered)索引,也叫聚簇索引,innodb的聚簇索引实际上是在同一个结构中保存了btree索引和数据行。 属于聚簇表示数据行和相邻的键值紧凑地存储在一起,因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
数据行的物理顺序与列值的顺序相同,如果我们查询id比较靠后的数据,那么这行数据的地址在磁盘中的物理地址也会比较靠后。而且由于物理排列方式与聚集索引的顺序相同,所以也就只能建立一个聚集索引了。
参考阅读

什么是死锁?死锁产生有哪些条件?

互斥条件,请求与保持条件,循环等待条件,不可剥夺条件

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值