查缺补漏

计算机基础

  • 平时所说的32位机器,就是能够同时处理字长为32位的电路信号。最左边表示正负,0表示正数,1表示负数。
  • 负数的补码时反码加1,这样可以使减法运算可以使用加法器进行运算。

java基础

  • 异或( ^ ): 相同则false , 不同则 true。
  • 短路或 ( || ): 会短路,若前半段式子为true, 则后面式子不成立。 “ | ” :是后面的式子依然会成立。
  • 对于数组: 实际内容存储在堆上,而实际内容的地址空间在栈上。
  • 按位与 &:都是 1 才是 1。
    • 应用场景:获取网段值,IP地址与掩码255.255.255.0进行按位与(&)运算得到高24位,即为当前IP 的网段。
  • 按位或 |:只要有 1 ,则为 1 。
  • getBytes( String str ) : 获取字符串的二进制形式。
  • Arrays的方法:
    • int binarySearch( long [ ] a,long key) 查找一个数
    • int[ ] copyOf( int [ ], int length) 数组复制
  • 引用型变量存储在栈上, 保存的是实际内容的地址,而实际内容保存在堆山。
  • 静态导入: static import 直接导入静态方法和成员
  • 单精度占4字节,双精度占8字节

sleep函数的运用

sleep 作为将当前线程挂起指定的时间

假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-4-7 12:00:01.000 的时候,这个线程会不会被唤醒?

答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。

某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是 Sleep 0 毫秒,那么他跟去掉这句代码相比,有啥区别么?

Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。

为什么需要两个survivor区

先来看一张图,假设没有Survivor区,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。

26

  • Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
  • 假设只设置一个Survivor区,当刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。堆空间被散布的对象占据不连续的内存,最直接的结果就是,堆中没有足够大的连续内存空间,接下去如果程序需要给一个内存需求很大的对象分配内存
  • 设置两个Survivor区上述机制最大的好处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片
  • 至于Survivor不分更多的区域,显然,若Survivor区再细分下去,每一块的空间就会比较小,很容易导致Survivor区满,两块Survivor区应该是经过权衡之后的最佳方案。

surviror的from区可以创建对象吗

在eden区创建,对象总在eden区出生,surviror的from区保存当前的幸存对象,to区则为空。

一次GC过程之后

  • eden区的对象+fron存储的对象 ——>复制到to区
  • 清空eden区的对象+fron存储的对象
  • 颠倒from区和to区的逻辑

线程池执行任务时,如何在某一个任务执行完毕返回结果,就立即停止线程池其他任务

  • signal
  • 主线程CAS自旋+shutdownnow
  • 设置一个容量为1的countdownlatch,然后countdownlatch返回后直接shutdownnow
  • 用一个对象承载每个子任务的thread,然后一个子任务完成后,处理线程中断
  • CompletableFuture 的cancel()操作

秒杀扩展

JVM(并发导致快速GC) +横向扩展+ 分布式压测

Volatile

一个线程在读取使用volatile修饰的变量时,会将该线程中所有使用的变量从主内存中读取。

详细解释:JVM会尽力保证从主内存同步数据,当不加volatile时,看线程情况,会尽力同步主内存的额数据到线程内存中。

volatile修饰数组,其实是数组的地址是线程可见的,实际上数组内元素看上去”线程内可见”,是因为每次获取元素时数组都是从主内存中获取地址信息,再根据index得到元素。

AtomicIntegerArray 和AtomicReference对元素实现了volatile的读写。

sleep会增加同步主内存数据的机会。

当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,强制走主内存。

高并发场景针对字典树

字典树固有的代码块

1
2
3
4
5
6
7
8
9
char[] letter=word.charArray();
for(int i=0;i
index=letter[i]-'a';
if(node.map[index]==null){
node.map[index]=new TrieNode();
}
node=node.map[index];

}

修改TrieNode类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Node{
boolean isEnd;
Node[] children;
public Node(){
isEnd = false;
children = new Node[26];
}

public synchronized void addChild(char c) {
children[c-'a'] = new Node();
}
public synchronized Node getChild(int idx){
return children[idx];
}
}

使用lock()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Node {
boolean isEnd;
Node[] children;
ReentrantLock[] locks = new ReentrantLock[26];
public Node(){
isEnd = false;
children = new Node[26];
for (int i = 0; i < 26; i++) {
locks[i] = new ReentrantLock();
}
}

public void addChild(char c) {
int idx = c-'a';
locks[idx].lock();

try{
if (children[idx]!=null){
return;
}
children[idx] = new Node();
}finally {
locks[idx].unlock();
}

}
public Node getChild(int idx){
Node res;
locks[idx].lock();
try{
res = children[idx];
}finally {
locks[idx].unlock();
}
return res;
}
}

增加读写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Node {
boolean isEnd;
Node[] children;
ReadWriteLock[] locks = new ReentrantReadWriteLock[26];
public Node(){
isEnd = false;
children = new Node[26];
for (int i = 0; i < 26; i++) {
locks[i] = new ReentrantReadWriteLock();
}
}

public void addChild(char c) {
int idx = c-'a';
locks[idx].writeLock().lock();

try{
if (children[idx]!=null){
return;
}
children[idx] = new Node();
}finally {
locks[idx].writeLock().unlock();

}

}
public Node getChild(int idx){
Node res;
locks[idx].readLock().lock();
try{
res = children[idx];
}finally {
locks[idx].readLock().unlock();
}
return res;
}
}

构成重载的条件

参数类型,参数个数,参数顺序不同

抽象类与接口

  • 抽象类可以没有抽象方法,接口必须有抽象方法
  • 接口中的都是常量,static final类型的,必须被初始化,抽象类可以有普通的成员变量
  • 抽象类只能单继承
  • JDK8之后接口有default方法,可以被实现

java和C++区别

  • java中不提供指针
  • C++的类可以多继承
  • java有内存自动管理机制

路由器

路由器是实现分组交换的关键构件,其任务是转发收到的分组,这是网络核心部分最重要的功能。分组交换采用存储转发技术,表示把一个报文(要发送的整块数据)分为几个分组后再进行传送。在发送报文之前,先把较长的报文划分成为一个个更小的等长数据段。在每个数据端的前面加上一些由必要的控制信息组成的首部后,就构成了一个分组。分组又称为包。分组是在互联网中传送的数据单元,正是由于分组的头部包含了诸如目的地址和源地址等重要控制信息,每一个分组才能在互联网中独立的选择传输路径,并正确地交付到分组传输的终点。

mysql中limit分页如何保证可靠性

如何监听tcp丢包的问题

http是否需要多次建立tcp连接

关于tcp连接

HTTP1.0协议不支持长连接,从HTTP1.1协议以后,连接默认都是长连接。

HTTP协议是基于请求/响应模式的,因此只要服务端给了响应,本次HTTP连接就结束了,或者更准确的说,是本次HTTP请求就结束了,下一次又是一个新的请求和新的响应,因此根本没有长连接这一说。那么自然也就没有短连接这一说了。

网络上说HTTP分为长连接和短连接,实际指的是TCP连接。TCP连接是一个双向的通道,它是可以保持一段时间不关闭的,因此TCP连接才有真正的长连接和短连接这一说。

如何理解HTTP协议是无状态的

HTTP协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)。

长连接、短连接

从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码: Connection:keep-alive 服务器和客户端都要设置

短连接一般只会在 client/server间传递一次读写操作。

短连接的操作步骤是:建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接。

ClassNotFoundException知道吗?遇到场景是什么?怎么解决的?

常见的场景就是:

  • 调用class的forName方法时,找不到指定的类

  • ClassLoader 中的 findSystemClass() 方法时,找不到指定的类

  • ClassLoader 中的 loadClass() 方法时,找不到指定的类

造成异常的常见原因:

  • 所需要的支持类库放错了地方,并没有放在类路径(CLASSPATH环境变量)里面。

  • 使用了重复的类库,且版本不一致。导致低版本的被优先使用。

  • 类名错了,一般是使用Class.forName的时候,手工指定了类名的情况。

  • 没有导入纯JAVA驱动包。

NoSuchMethodException遇到过吗?

反射时候会出现,查了下Class的文档,该类下原来有两个方法:getMethod,getDeclaredMethod。getMethod只能调用public声明的方法

getDeclaredMethod基本可以调用任何类型声明的方法

0%