如下代码:

1
2
3
4
5
6
7
final int start = Integer.MAX_VALUE-100;
final int end = Integer.MAX_VALUE;
int count = 0;
for (int i = start; i <= end;i++) {
count++;
}
System.out.println(count);

idea调试

输出结果为?

没有输出结果

分析

Integer.MAX_VALUE用二进制表示1111 1111 1111 1111 1111 1111 1111 111

所以当i++一直到=end时,即i = Integer.MAX_VALUE时,此时计算的结果为100,但是当接着往下面执行时,i++即 i = Integer.MAX_VALUE+1,i此时二进制1111 1111 1111 1111 1111 1111 1111 111 + 1 的结果为-1000 0000 0000 0000 0000 0000 0000 0000,即i = -2147483648,符合 i < end条件,所以循环接着往下执行,一直到=end,+1后又变为负数,周而复始,所以不会有结果输出。

1
2
3
4
5
6
7
8
9
10
HashSet<Short> set = new HashSet<>();
for (short i = 0; i < 100; i++) {
// set.add set添加的类型为short
set.add(i);
// 通过计算得知 int i1 = i - 1,i-1返回的是一个int类型的值,所以remove时找不到需要remove的值
set.remove(i-1);
}
// 从而size是100
System.out.println(set.size());

tcp 7层

  • 应用层
  • 表示层
  • 会话层
  • 传输层
  • 网络层
  • 数据链路层
  • 物理层

4层网络模型:

其中应用层、表示层、会话层表示应用层,数据链路层、物理层表示网络接口层

ARP协议

复杂的程序都需要分层,软件开发设计必须要做的

tcp负载均衡

二层负载 -> mac地址,vip(虚拟ip) 多个机器IP相同但是mac地址不同

三层负载 ->ip 对外提供虚拟ip,集群中不同机器采用不用的ip,IP请求转发

四层负载 -> 传输层负载,包含ip和端口,修改目标ip和端口地址

七层负载 -> 应用层负载,请求的url,http请求的报文,如http://a/a.html,主机名。

tcp和udp区别

TCP/IP:可靠

  1. 建立连接机制
  2. 三次握手建立连接
    1. client发送请求连接的数据包
    2. server返回可以进行连接的数据包
    3. client发送确认请求连接的数据包
    4. client/server可以进行数据传输了
  3. SYNC攻击 -> 客户端伪造大量ip和mac地址发送消息给服务器,占用服务器大量资源
  4. 连接的关闭,四次挥手协议
    1. client发送请求关闭连接的数据包
    2. server返回确认收到请求关闭的数据包-此时server可能还处于数据操作的状况
    3. server数据已经处理完毕,并返回可以关闭的数据包
    4. client返回最终请求关闭数据包-此时server接收到数据包以后将处于关闭状态,而client可能还处于数据为未接受完的状态,等待2个固定的时间点后进入关闭状态
  5. TCP是一种全双工的协议
  6. 长连接,正常连接是超时关闭,但是长连接可以发送心跳包维持连接

UDP/IP:不可靠

在应用中如何去构建一个传输的实例

socket -> 套接字

socket io

file io

通讯过程中的阻塞

accept阻塞(连接阻塞)

流操作的阻塞(read/write)

滑动窗口的协议

流量控制

BIO模型

​ blocker io 阻塞

优化方案:

可以解决socket io阻塞,但是还是存在file io阻塞问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ServerSocket server = new ServerSocket(8080);
while(true) {
Socket socket = server.accpet();
new Thread(new SocketThread(socket)).start;
}

public class SocketThread implements Runnable{
Socket socket;
public SocketThread(Socket socket) {
this.socket = socket;
}
public void run() {
// 对socket进行操作
}
}

NIO

非阻塞

​ new io / non blocker io

多路复用机制

linux fd:linux系统中一切皆可看成是文件,文件描述符是内核为了高效管理已被打开的文件所创建的索引

serverSocket 向请求内核

先监听socket/fd,再如果发现socket/fd已就绪,就发送数据已经准备好的通知(即可以像该文件进行读写),然后serverSocket再来发送read/write请求

linux提供的io复用机制

select/poll:轮询查询socket/fd是否已经就绪

epoll:事件机制,理解为注册监听机制,如果发现socket/fd就绪直接返回就绪通知,可以进行读写

服务端的机器怎么去标记一个个的连接

四元组:source_ip/source_port/target_ip/target_port

因为目标机器的ip和端口固定,所以最大连接数取决于客户端的数量

source_ip : 2^32

source_port:2^16 因为0不能作为端口,所以是65535个

  1. 内存大小
  2. 文件句柄的限制
  3. ulimit -n 默认是1024
  4. 带宽的资源

jdk1.8 时间的创建及工具类

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
// Clock
Clock clock = Clock.systemDefaultZone();
System.out.println(clock.millis());
Instant instant = clock.instant();
System.out.println(instant);
Date from = Date.from(instant);
System.out.println(from);
// 时区
// 获取所有时区
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
System.out.println(availableZoneIds);
// 获取指定时区的ZoneId
ZoneId of = ZoneId.of("Asia/Aden");
System.out.println(of.getRules());

// localtime 获取指定时区的时间
LocalTime now = LocalTime.now(of);
System.out.println("当前时间是:"+now);

// localData 本地日期
LocalDate today = LocalDate.now();
System.out.println("今天的日期是:"+today);
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
System.out.println("明天的日期是:"+tomorrow);
LocalDate yesterday = today.plus(-1, ChronoUnit.DAYS);
System.out.println("昨天的日期是:"+yesterday);
// 创建指定日期
LocalDate of1 = LocalDate.of(2019, Month.JUNE, 11);
System.out.println(of1);
DayOfWeek dayOfWeek = of1.getDayOfWeek();
System.out.println("今天是周几:"+dayOfWeek);
// DateTimeFormatter格式化时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
String format = formatter.format(today);
System.out.println(format);

Stream

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
List<String> stringList = new ArrayList<>();
stringList.add("ddd2");
stringList.add("aaa2");
stringList.add("bbb1");
stringList.add("aaa1");
stringList.add("bbb3");
stringList.add("ccc");
stringList.add("bbb2");
stringList.add("ddd1");

// stream 串行
// 过滤
stringList
.stream()
.filter(s -> s.startsWith("a"))
.forEach(System.out::println);
System.out.println("-----------------------");

// 排序
// 排序只是创建了一个排序好的stream,原数据并没有改变
stringList
.stream()
.sorted()
.filter(s -> s.startsWith("a"))
.forEach(System.out::println);
System.out.println("-----------------------");

// map映射,map操作
stringList
.stream()
.map(String::toUpperCase)
.sorted((o1, o2) -> o2.compareTo(o1))
.forEach(System.out::println);
System.out.println("-----------------------");

// 匹配,任一一个匹配就可以返回
boolean anyMatch = stringList
.stream()
.anyMatch(s -> s.startsWith("a"));
System.out.println(anyMatch);
System.out.println("-----------------------");

// 匹配,所有匹配,所有匹配到才会返回true
boolean allMatch = stringList
.stream()
.allMatch(s -> s.startsWith("a"));
System.out.println(allMatch);
System.out.println("-----------------------");

// 匹配,没有匹配到的返回true,如果有匹配到的则返回false
boolean noneMatch = stringList
.stream()
.noneMatch(s -> s.startsWith("a"));
System.out.println(noneMatch);
System.out.println("-----------------------");

// 计数
long count = stringList
.stream()
.filter(s -> s.startsWith("a"))
.count();
System.out.println(count);
System.out.println("-----------------------");

// 规约,将集合整合为一个元素
Optional<String> reduce = stringList
.stream()
.sorted()
.reduce((s, s2) -> s + "#" + s2);
reduce.ifPresent(System.out::println);

lambda语法创建接口

1
2
3
4
5
6
7
8
9
10
11
public interface InteTest {
void initData(String string);
}
public class Test {
public static void main(String[] args) {
InteTest inteTest = string -> {
System.out.println(string);
};
inteTest.initData("123");
}
}

soa和微服务的区别

soa:面向服务的框架,着重于将应用系统根据不同的业务模块拆分不同的服务,并通过这些服务定义好的接口联系起来。它是一种粗粒度,松耦合的框架,主要解决的是服务重复性问题。

微服务:微服务是soa架构下的最终产物,它相对soa来说对系统的拆分更加彻底,拆分粒度更细,拆分后的服务都可以单独的运行。微服务主要解决的是服务的高耦合问题,通过降低服务之间的耦合度来把服务分割的更彻底,每个服务都可以不依赖于其他服务而独立运行,当一个服务出问题时,对其他服务并不会造成影响。

soa和微服务都是分布式的具体实现思想。

你是怎么理解微服务的?

微服务顾名思义就是将系统拆分为一个个可以独立部署、水平扩展、独立运行的细微服务。

使用微服务的思想通过对单体架构的系统进行细粒度的拆分,从而降低各个服务之间的耦合度,单个服务中如果出现问题而不会对其他服务产生影响。并且通过对系统的拆分,会极大的解决原单体架构系统中的有些业务运行速度过慢而对其他业务造成的影响。

什么是SpringCloud

SpringCloud可以通俗的理解为整合各个分布式基础设施的工具箱,它是一系列框架的有序集合,注重于服务治理。

微服务架构的优点和缺点有哪些?

微服务的主要作用是大型业务系统进行细粒度的拆分,从而降低项目开发时某一模块的开发对其他模块业务开发的影响,可以将项目中的各个模块更快地上线落地,相对传统的单体架构中一块出问题其他都会被影响,微服务带来的是更加敏捷的更新维护,当其中一个服务出问题时,其他服务不会受到影响。但是当系统拆分的太细致,造成的运维成本也相对的更大,梳理各服务的功能调用逻辑更麻烦,本来只需要对一个系统的维护现在变相的变成对多个系统的维护,这样的成本会大大的增大。

SpringCloud解决了什么问题?

SpringCloud利用SpringBoot的开发便利性简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、熔断器、数据监控、负载均衡等,都可以利用SpringBoot的开发风格做到一键部署和启动。

并发编程

MESI协议

基于失效的缓存一致性协议

有四个状态:

  • Exclusive(E):独享,其他缓存都没有有效的副本
  • Shared(S):共享,其他缓存都有有效的副本
  • Invalid(I):失效
  • Modified(M):修改

原子性、有序性、可见性

原子性

在java中,对基本数据类型的变量读取和赋值操作是原子性操作,这些操作是不可中断的,也就是要么读,要么不读,或者要么写要么不写。

1
2
3
4
5
x = 10; // 操作1
y = x; // 操作2
x++; // 操作3
x = x + 1;//操作4

以上的操作中只有操作1属于原子性操作。

有序性

有以下这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int x = 10;
//volatile int x = 10; 如果x有volatile关键字修饰时,就可以保证有序性
void cpu0(){

x = 100;// 此时x的状态变幻情况为:S(共享) -> M(修改),此时通过cpu0会通知其他cpu的x的值为I(失效),再取出最新值,会消耗一段时间才会将x的值修改完成
flag = true;// 当flag此时是E状态时,变化状态:E(独享) -> M(修改),此时flag的值会直接修改,并发送给其他cpu状态置为S(共享)
}

void cpu1(){
while(flag) {
System.out.println(x == 100);
}
}

当有volatile关键字修饰时,会看到在程序运行时的汇编指令中会发现有locak指令,相当于一个内存屏障,会强制把缓存的操作写入主存。

当不能保证有序性时,打印输出的结果可能为false。所以在程序运行必须保证有序性。

可见性

在Java中,提供了volatile关键保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
此外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

volatile、synchronized

volatile可以保证对变量操作的有序性、可见性,但是不能保证原子性

synchronized可以保证对变量的有序性、可见性、原子性

linux的5种io模型

阻塞式io

非阻塞式io

信号驱动式io

io复用模式

异步io

URL、URI的区别

定义

URL:统一资源定位符

代表的是一个具体的资源位置,如http://localhost:8081/spring/demo/query?name=1

URI:统一资源标识符

用于标识互联网资源名称的字符串,URI最常见的形式是URL,所有URL是URI的子集,URI属于URL更好层次的抽象,一种字符串文本标准。如/spring/demo/query,着重于表示资源的名称

组成

URI

Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的.

URL

URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录

一个URL例如:http://localhost:8081/spring/demo/query?name=1

组成部分为:schema://host[:port]/path/…/?[url-params]#[query-string]

包含以下组成部分:

  • schema:指定应用层使用的协议,例如:http,https,ftp
  • host:http服务器的域名或ip
  • port:端口,可选项,80端口可以不填,如果使用了其他端口,则必须说明
  • path:请求的路径
  • query-string:请求参数

Arrays.asList()的使用

代码:

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
39
40
public class ArraysToListTest {
public static void main(String[] args) {
int[] nums = {1,2,3,4,5};
printArrays(nums);
List<int[]> ints = Arrays.asList(nums);
printArrays(ints);


Integer[] integers = {1,2,3,4,5};
printArrays(integers);
List<Integer> integerList = Arrays.asList(integers);
printArrays(integerList);
}

public static void printArrays(List list){
list.forEach(o -> {
if (o instanceof int[]) {
printArrays((int[]) o);
}else {
System.out.println(o);
}
});
System.out.println();
}

public static void printArrays(Integer[] nums){
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
System.out.print(num+" ");
}
System.out.println();
}
public static void printArrays(int[] nums){
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
System.out.print(num+" ");
}
System.out.println();
}
}

总结:

当使用Arrays.asList(nums);转换的数组内容为基本数据类型时,转换后的list集合的存放的也是这个基本数据类型的数组,而不是这个基本数据类型的值。

如果要对转换后的list进行增删改操作,则需要以new的方式来创建list

解决方法:

使用其包装类型:

1
2
3
Integer[] integers = {1,2,3,4,5};
List<Integer> integerList = new ArrayList<>(Arrays.asList(integers));

Integer

有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Integer i1 = 127;
Integer i2 = 127;
System.out.print(i1+" == "+i2+" :"+(i1 == i2));
System.out.println(" "+i1+".equals("+i2+") :"+(i1.equals(i2)));

Integer i3 = 128;
Integer i4 = 128;
System.out.print(i3+" == "+i4+" :"+(i3 == i4));
System.out.println(" "+i3+".equals("+i4+") :"+(i3.equals(i4)));

Integer i5 = new Integer(127);
Integer i6 = new Integer(127);
System.out.print(i5+" == "+i6+" :"+(i5 == i6));
System.out.println(" "+i5+".equals("+i6+") :"+(i5.equals(i6)));

Integer i7 = new Integer(128);
Integer i8 = new Integer(128);
System.out.print(i7+" == "+i8+" :"+(i7 == i8));
System.out.println(" "+i7+".equals("+i8+") :"+(i7.equals(i8)));

Integer i9 = new Integer(40);
Integer i10 = new Integer(40);
Integer i11 = new Integer(0);
System.out.println("i9=i10+i11 " + (i9 == i10 + i11));

输出结果如下:

1
2
3
4
5
127 == 127 :true    127.equals(127) :true
128 == 128 :false 128.equals(128) :true
127 == 127 :false 127.equals(127) :true
128 == 128 :false 128.equals(128) :true
i9=i10+i11 true

总结:

  1. 在-128~127的Integer值如果以Integer x = value;的方式赋值的Integer值在进行==和equals比较时,都会返回true
  2. 在-128~127之外的以Integer x = value;的方式赋值的Integer值在进行==和equals比较时,返回结果并不下相同
  3. 无论是在-128~127还是之外的Integer值如果以Integer x = new Integer(value);的方式赋值的Integer值在进行==和equals比较时,返回结果都并不x相同
  4. 因为Java里面对处在在-128127之间的使用Integer x = value;赋值的Integer值,用的是原生数据类型int,会在内存里供重用,也就是说这之间的Integer值进行==比较时只是进行int原生数据类型的数值比较,而超出-128127的范围,进行==比较时是进行地址及数值比较。
  5. 而对于所有以Integer x = new Integer(value);赋值的Integer值,使用== 比较时是进行地址及数值比较。
  6. 语句 i9 == i10 + i11,因为+这个操作符不适用于 Integer 对象,首先 i10 和 i11 进行自动拆箱操作,进行数值相加,即 i9 == 40。然后 Integer 对象无法与数值进行直接比较,所以 i9 自动拆箱转为 int 值 40,最终这条语句转为 40 == 40 进行数值比较
  7. 所以在比较包装类型的值时,必须使用equals进行比较
0%