java 课程学习笔记上

慕课网
Java校招面试 Google面试官亲授

操作系统

程序会运行在不同的机器上,常见的服务器,台式机,笔记本,手机。还有其他的一些设备,电视机机顶盒,还有其他的智能家居,我们要给这些设备写程序就不可避免地要给这些设备的物理接口打交道,这个打交道的过程是非常麻烦复杂的。我们在开发程序的时候需要关注的是业务逻辑上面,而不是和业务逻辑没有关系的细节上面,因此就有了操作系统,它在我们开发的程序和设备之间,我们的程序只需和操作系统,和设备打交道的复杂内容就交给了操作系统,虽然理论上这么说,我们在开发过程中不可避免的要和操作系统的细节打交道。

进程和线程

英文一个是process thread,这里主要从构成讲起:

进程

在操作系统的边界是比较明显的,进程和进程之间相对来说是独立的,进程里有很多线程,内存也是在进程里面,逻辑内存4G, 8G,32位,操作系统的寻址空间是4g,所以32位的操作系统每个进程都会有4g的内存空间,意思是有4g的内存空间,进程可以用,不是就把4g的空间分给某个进程了,每个进程的内存都是互相独立的。不然的话,就可以看多别的进程的重要信息,所以显然不可能,句柄在英文里是handler,文件和网络资源和内存不一样,他们是所有的进程所共有的,可以去抢同一个网络的端口。

线程

线程里有栈,从主线程的入口main函数,他会不断地进行函数调用,每次调用会把所有的参数和返回地址压到栈里面去,包括函数的局部变量也会放进栈里,线程还有一个PC,下一条指令的地址,操作系统(cpu)在运行的是一个一个线程,进程只是容器,pc指向一个指令, 每个pc都会有指针指向自己代码的内存,数据和程序是存储在同一片内存里的,一个漏洞叫缓冲区溢出,有存储数据的地方存储用户名,黑客把用户名输得特别长,长度超出了数据缓冲区,到了存储程序的那部分内存里了,黑客可以把想要运行的代码通过输入用户名的框植入进来。TLS, 可以在tls中存储变量,这些数据是线程独有的数据。

线程是操作系统真正运行的, 进程是一个容器,把一部分相关的东西放在了一起,旁边放了一个很强的隔离,把不同的程序隔离开来。

进程间交互比较常见的就是通过TCP/IP的端口来实现。

线程间通讯可以通过共享内存。

寻址

存储:

操作系统的存储是结构化的。

寻址空间:

和电脑上装了多少内存没有关系,和有多少进程也没有关系,每个进程有自己独立的寻址空间。32位操作系统,进程的寻址空间就是4g,因此大部分系统是64位的,2的64次方很大。64位jvm可以使用更大内存,把32的java程序迁移到64 位的jvm上面,绝大多数情况下只需要重新编译下就行了。

如果找一个指针p,逻辑内存是2的32次方或者2的64次方。指针p有可能指向物理内存在,也可能指向虚拟内存中。寻址的时候需要把虚拟内存的地址放在物理内存中,寻址时如果一个一个比较,太耗费系统资源,把p所在的分页放在物理内存,分页大小取决于操作系统。由算法把物理内存中不用的一部分放在虚拟内存中(交换),腾出物理内存的空间。在windows中可以设置虚拟内存的大小和位置。如果物理内存使用量过多,就会发生频繁的分页,硬盘是很慢的,系统就会变慢。

网络

在不可靠不安全的线路上, 建立一个可靠安全的数据传输渠道,搭了七层架构在实现。

应用层 表示层 会话层 传输层 网络层 数据链路层 物理层,(应用层在最上面,物理层在最下面)

数据链路层:数据包是最小传输单位,一个数据包可以通过奇偶校验和其他校验方法来看是不是正确。如果传给其他学校或者国家,就需要网络层,网络层会有路由,一个电脑先发给附近的路由器,路由器再发给路由器网络,为了标识网络中的各个节点,使用了ip协议。

传输层有tcp协议和udp协议,tcp协议是基于连接的。数据为了哪个应用服务的呢?是http ftp还是email的协议?这些协议就是应用层的协议。从不可靠不安全的物理层到到应用层,7层协议中有两层会被淡化。

不可靠:丢包,重复包 出错 乱序 TCP协议中对不可靠都给出了解决方案

网络的传输一定是不安全的原因,因为会经过很多路由。

在数据链路层电脑发送数据到路由器,在网络层发给目的地

滑动窗口

在TOP 协议中,滑动窗口维持发送方和接收方缓冲区

最开始发送和接收是发送一个数据包,接收一个数据包,如图:
这里写图片描述

改进过后,一次发送两个,(上限就是两个),然后接收确认信息

这里写图片描述
滑动窗口:
这里写图片描述

确认4号包后,发送8号和9号
这里写图片描述

如果5号包一直没有传过来Ack
这里写图片描述

如果5号包一直没有传来确认信息,就会一直等待,接收方不会提前把6、7号的Ack发送过来,会一直等5号,如果超时,发送方会重新发送5号包,这时接收方可能会把5、6、7三个包的Ack一起发过来。

为了增加线路的吞吐量,把包一起发,

http协议端口为:80

访问慕课网

DNS 请求

DNS 包,第二行Ethernet 数据链路层的报文头,第三行Internet Protocaol version 4 网络层IP 层的头,源是自己的ip地址,目的地是DNS的服务器。 第四行是UDP的头,对DNS的请求,是作为一个UDP包发出去的,不需要预先地建立连接,第五行是DNS 的内容。每一个层次的网络协议都是它上一个层次的数据的部分。

数据链路层, 数据源(src) 是自己的笔记本,dst目的地是无线路由器,这个数据包是DNS的一个query,问路由器是问不到的,最终是要传到外面的DNS server

DNS 响应

熟路链路层的源是路由器,目的地是自己的电脑,ip层是从DNS Server 发送到自己192.168.0.108,response 里面会把query 重复一遍,接下来是answer, answer 里面有三个地址,然后电脑选择了第一个地址,知道了ip地址后就要建立TCP连接,

TCP 连接

第一个包叫SYN , 第二个包叫SYN ACK, 第三个包叫ACK, 这是三次握手。

第一次握手:

在数据链路层这些包仍然都是发送给路由器的,在网络层才是发给慕课网的,因为网络层比数据链路层高一层,电脑想要和慕课网通讯,先要在网络层通过ip协议到慕课网的ip,然后具体实施的第一步就是把数据传送到路由器。destination port 是80, 说明我们希望和慕课网的80端口作连接,80号端口就是HTTP协议的端口,sequenve number 是包的编号,这是包的编号是0,acknowledgment number 是0, 因为现在没有收到任何包来ack,flags 是syn,syn表示连接的请求,windows size value(滑动窗口的大小 ) 是65535, TCP Segment Len 是0,这个包只带了一个头部,tcp的内容是0,所以这个包的长度除去头部是0。

第二次握手:

慕课网回过来的包:acknowledgment number 是1,意思就是期待发送第一号包,因为我的电脑刚才发送的是0号包,所以慕课网期待我发送第1号包,所以acknowledgment number 是1。 flags 被置了(SYN, ACK)代表慕课网接到了链接请求,慕课网也愿意建立链接,

第三次握手:

sequence number 是1, acknowledgment number 也是1, 说明我收到了慕课网的0号包,期望慕课网的1号包。然后我调整了窗口的大小为4105, 然后又有个calculated windows size: 131360 还有一个scaling factor: 32,131360=4105*32. 第一个包中有个option,里面有一个字段叫window scale:5,这个含义就是乘以32。当电脑把第三个ack发过去后,第三次握手就完成了。tcp连接就建立了,发完第三个ack之后就发送HTTP请求。

HTTP包请求

sequence number 是1, TCP segment len 是403, 那么Next sequence number 就是404,然后ack 是1.

HTTP包回复

sequence number 是1 , 这是慕课网的sequence number, ack 是404,表示从0到403已经收到了,希望我的电脑发送的下一个包从404开始发。

接下来会发送一些TCP segment of a reassembled PDU 包,它是很大的,一个包装不下,就拆分成了很多个包,每个包拆分成了1494的大小 ,然后可以看到TCP Segment Len: 1428 所以sequence number 是1, next sequence number 就是1429 同时ack 是404,表示0 到403都接收到了。TCP Segment data 1428 bytes,其中有一个字段是encoding: gzip, 接下来的数据都是乱码,并不是以明文的方式传递的,而是以gzip传输的,传到浏览器后,浏览器会解压。下一个包还是1494,Sequence number 是1429, Tcp Segment Len:1428,next sequence number 是1494+1428=2857.

接收到两个从慕课网发过来的包的时候,我就会回复,我的sequence number 是404, ack 是2857,说明一口气收到了两个包。

中间可能建立新的TCP连接,是为了获取其他东西,比如刚刚得到了html部分,这次可能就是请求css部分。建立了另一个TCP后,两个连接数据会混在一起,有一个stream index,上一个是18,现在这个是19.

例子:

抓包后发现win=36,但是len=180,初看这个不符合滑动窗口的规则。

解释:由于TCP的头部窗口字段只有16bit,最多表示64k,为了表示更大的窗口,使用了可选的放大倍数。

  1. 在TCP三次握手的时候在SYN或SYN,ACK(第一个或第二个)包中,通知options可选信息,告知对方将使用放大倍数。
  2. SYN本身不放大
  3. 所以windows size value 表示报文的值,calculated Window size 表示放大后的值,也就是实际可用的值。

在不包含SYN的包的tcp文件中,wireshark不知道放大倍数,显示-1(unknown),

segment of a reassembled pdu

这个只是wireshark 的提示信息,主机相应一个查询或命令时需要回应很多数据,而这些数据超过了TCP最大MSS时,主机会通过多个数据包发送这些数据,对wireshark来说这些对相应同一个查询命令的数据包被标记了TCP segment of a reassembled PDU.

wireshark 如何识别多个数据包是对同一个查询数据包的相应? 这些数据包的ACK number 是相同的, 数值和查询数据包中的next sequence number 是一样的。

MTU: Maxinum Transmission Unit 最大传输单元

MSS: Maxinum Segment Size 最大分段大小

PPPoE: PPP Over Ethernet 在以太网上承载PPP协议

MTU: 和数据链路层有关,每一个以太网帧有最小64bytes 最大1518bytes,小于最小或大于最大都是错误的,一般的以太网转发设备会丢弃这些数据帧。(小于64bytes可能是以太网冲突产生的碎片,或者线路干扰或坏的以太网接口产生的,大于1518bytes的数据帧被称为Giant 帧,可能是线路干扰或者坏的以太网接口产生的),刨去各种头(DMAC 目的MAC地址6bytes 和SMAC源MAC地址6bytes和type域2bytes和帧尾的CRC校验部分4bytes),剩下的最大就是1500bytes,我们就叫1500bytes 是MTU,网络层协议IP协议会根据这个值把上层传下来的数据进行分片。

当两台远程PC互联的时候,数据会穿过很多路由器,就好比一个长的水管,有粗有细,通过这段水管最大水量就要由中间最细的水管决定。对于网络层的上层协议来说,不关心水管粗细,这是网络层的事情。网络层的IP协议会检查每个从上层协议传下来的数据包的大小,并根据本机的MTU的大小决定是否“分片”,分片的最大坏处是降低了传输的性能,本来一次就搞定的事情,需要多次。有些高层因为某些原因要求不能分片,会在IP数据包包头加上DF(Donot Fragment)。这样当遇到MTU小于IP数据包的情况,转发设备会根据要求丢弃这个数据包,然后返回一个错误信息给发送者。这样往往会造成某些通讯的问题,不过大部分网络链路都是MTU1500bytes或者大于1500bytes。

对于UDP,这个协议是无连接的协议,对于数据的到达顺序以及是否到达不关心,所以一般UDP应用对于分片没有特别要求,而TCP就不同了,这个面试连接的协议,非常在意数据包的到达顺序以及传输中是否有错误发生,所以有些TCP应用对分片有要求–不能分片(DF)

MSS: 就是TCP 数据包每次能够传输的最大分段,为了达到最佳的传输性能,在建立连接时通常会协商双方的MSS,这个值通常会被MTU值代替。(需要减去IP头和TCP头各20bytes),所以MSS为1460

例题

TCP 采用的滑动窗口大小不是固定的可以到几万(65535),除了流量控制,还参与了一部分拥塞控制,在传输过程中会调整窗口的大小,大小为0是合法的。

阿里巴巴有两个相距1500km的机房A和B,现在有100GB的数据要通过一个FTP连接在100s内从A传输到B,已知FTP连接建立在TCP协议上,而TCP协议通过ACK来确认每个数据包是否正常发送,网络信号传输速率为2*100000km/s,假设机房宽带足够高,那么A节点的发送缓冲区最小可以设置为

1500*2 / 2*100000=0.015s,这是说从A发送数据到A接收确认最短需要0.015s,而100GB在100s内发送完,速率最小可以在1GB/S,在0.015s内发送15MB信息,所以发送缓冲区理论上最小为15MB,实际上要大一些,可以为18MB

另一种理解:

一个来回的事件0.015s, 来回次数最多:100/0.015=6666.667次,我们数据由100GB,每次传输至少100GB/6666.667 = 15MB, 所以缓冲区最小可以为18MB.

数据库简介

这些年非关系型数据库很火,noSQL越来越流行,但是关系型数据库任然占有统治地位, 如果去开发一个行业软件,很可能会遇到一个历史遗留的非常庞大的关系型数据库,我们还是要在上面做开发。

基于关系代数理论。之前说到的网络,网络是一个纯粹实践性的学科,它没有理论,它就是有一个不可靠的连接,在这上面不断发现问题,解决问题,发现新的问题,再解决新的问题,一级一级解决下去,一共搭建个七层架构,最终把问题基本解决了。几十年前就有人提出了关系代数的理论。由第一范式、第二范式、第三范式、BC范式、第四范式、第五范式。基于这些理论可以给现实生活中基本上所有结构进行建模,然后根据这个开发出关系型数据库各种产品。

一个二维表(显示生活中人们非常习惯用二维表描述问题,不是所有结构都是可以通过二维表直观的描述的,关系代数说:即使有一些不直观的描述,也能通过二维表来描述,所以就开发了很多关系型数据库的产品,结果就是实现复杂,速度慢。它通过二维表的外键,由数据库系统保障健壮性,所以数据只要能写进去,我们对数据的正确性和一致性都是很有把握的):

缺点:表结构不直观,实现复杂,速度慢。

优点:健壮性高,社区庞大。

近些年发现对健壮性的要求没有那么高,尤其是采用分布式的系统,关系型数据库的健壮性是针对一个节点来说的,当我们使用多个节点,健壮性也不存在了。所以我们宁可用健壮性低一点的产品,我们可以拥有直观的结构,更简单的实现,更高的性能。

join

category 中主键是product 的外键

如果在product 表中插入一个记录,而外键在category 中没有,那么会失败。

select * from product join category 只写join 就是一个笛卡儿积,结果是2*4 = 8条记录

内联接(不显示null):

select * from product p join category c
on p.categoryId = c.categoryId;

左外连接,右外连接(显示null),

可以在聚合函数(count,min)后面使用group by。

SELECT application, count(*)from logs group by application

SELECT application, min(timestamp) from logs group by application;

select p.* cat_min.categoryName from product p join (

select p.categoryId, categoryName, min(p.price) as min_price from product p left join category c

on p.categoryId = c.categoryId

group by p.categoryId, categoryName) as cat_min

on p.categoryId = cat_min.categoryId

where p.price = cat_min.min_price

事务

  • atomicity 原子性
  • consistency 一致性
  • isolation 隔离性 (最关键)
  • durability 持久的

  • 原子性: 保证任务中所有操作都执行完毕;否则,事务会出现错误时终止,回滚之前所有操作到原始状态

  • 一致性: 如果事务成功执行,则数据库的状态得到了进行了正确的转变
  • 隔离性: 保证不同的事务相互独立
  • 持久性:即使出现系统故障,之前成功执行的事务结果也会持久存在

事务的隔离级别

  • read uncommitted 可以读到没有提交的值,commit 之前可以Rollback
  • read committed 两次读,值可能会发生改变
  • repeatable reads 在一个事务中,一开始读的是49,那么一直读的都是49
  • serializable 事务同时发生的结果一定是先后发生中的一个

update product where set count=count-1 where productId = 2;

我们把上一句话分成两部分,读count 和写count 分开。我们把autocommit 关掉

begin;
set autocommit=0;
select @@tx_isolation;
select count from product where productId=2;

update product set count =49 where productId=2;

现在isolation 是read_committed, 如果一个用户把count update 为49,没有commit, 那么另一个人读还是50.

如果commit了,读的是49。

从isolation 角度来说我们的事务不被外界影响,一开始读的是多少就永远是多少,这就是repeatable reads,但是还是没有解决并发问题。

再提高一个级别到 serializable,我们在一个窗口中读,读出来是48(这时没有结束),然后在另一个窗口把48改为47,结果修改不成功,当我们的窗口完成update

并发问题的本身在于读上面要加锁,读完后,不希望别人读它。

所以加上for update select count from product where productId=2 for update;

乐观锁:

冲突机会不多的时候可以使用乐观锁,

update product set count= 46 where productId=2 and count=47;

抢票很可能是在非关系型数据库中,在内存中进行的。

改善数据访问方式以提升缓存命中率 是对的

使用多线程的方式提高I/O密集型操作的效率是错的

使用数据库连接池代替直接数据库访问 是对的

使用迭代替代递归 是对的

合并多个远程调用批量发送 是对的

共享冗余数据提高访问效率 是对的

猜你喜欢

转载自blog.csdn.net/LEIQINGQI/article/details/81546061