@yiranblade
2018-04-26T04:37:10.000000Z
字数 29315
阅读 390
春招/实习
32位端口号:源端口号和目的端口号各占16位
32位序号:也称为顺序号,简写为SEQ
32位确认序号:也称为应答号,简写为ACK。握手阶段,确认序号将发送方的序号+1作为回答。
4位首部长度:TCP的头长度最长可为60字节
6位标志字段:ACK置1时表示确认号,RST置1时重建连接。如果接收到RST位的时候,通常发生了某些错误。SYN置1时用来发起一个连接。FIN置1时表示发送端完成发送任务。PSH置1表示请求的数据段在接收方得到后就可直接送到应用程序,不必等到缓冲区满时才发送。
16位校验和
16位紧急指针
可选与变长选项(最常见的可选字段是最长报文大小,又称为MSS)。
2字节源端口字段:源端口是一个大于1023的16位数字;
2字节目的端口字段
2字节长度字段:指明了包括首部在内的UDP报文段长度。
2字节校验和字段
| 特征 | TCP | UDP |
|---|---|---|
| 可靠性 | 可靠 | 不可靠 |
| 连接性 | 面向连接 | 无连接 |
| 报文 | 面向字节流 | 面向报文 |
| 效率 | 传输效率低 | 传输效率高 |
| 双工性 | 全双工 | 一对一、一对多,多对一,多对多 |
| 流量控制 | 有(滑动窗口) | 无 |
| 拥塞控制 | 有(慢开始、阻塞避免、快重传、快恢复) | 无 |
| 传输速度 | 慢 | 快 |
| 应用场合 | 对效率要求比较低,但对准确性要求比较高 | 对效率要求比较低,对准确性要求相对低的场景 |
| 应用示例 | TCP一般应用于文件传输(FTP,http对数据准确性要求高,速度可以慢),发送或接收邮件(pop,imap,smtp对数据准确性要求高,非紧急应用),远程登录对数据准确性有一定要求,有连接等 | UDP一般用于即时通信(QQ聊天对数据准确性和丢包要求比较低,但速度必须快),在线视频(rtap速度一定要快,保证视频连续,但是偶尔花了一个图像帧,人们还是能接受的),网络语音电话(VOIP语音数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题)等等。 |
整个过程使用文字描述如下:
首先A的TCP客户进程向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序列号seq=x。此时A的客户进程就进入了一个SYN-SENT(同步已发送状态)。
B收到连接请求报文段后,向A发送确认。在确认报文段中把SYN和ACK都置为1,确认号是ack,同时也为自己选择一个初始序号seq=y。此时B的TCP服务器进程就进入SYN-RCVD(同步已收到)状态。
A的TCP客户端收到B的确认后,还要向B给出确认。确认报文段的ACK置为1,确认号ack=y+1,而自己的序号等于seq=x+1。这时TCP连接已建立,A进入ESTABLISHED(已建立连接)状态。
当B收到A的确认后,也会进入ESTABLISHED状态。
这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。
整个过程使用文字描述如下:
数据传输结束后,通信双方都可以释放连接。现在A和B都处于ESTABLISHED状态。A的应用进程先向TCP发出连接释放报文段,主动关闭TCP连接。A把连接释放报文段的首部的终止控制位FIN置为1,序号seq=u,它相当于前面已经传送过的数据的最后一个字节的序号加1。这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。
B收到连接释放报文段后即发出,确认号是ack=u+1,而这个报文段自己的序号是V,等于B前面已经传送过的数据的最后一个字节的序号加1。然后B就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时通知高层应用进程,因而从A到B这个方向的连接释放了,这时的TCP连接处于半关闭状态,即A已经没有数据要发送了,但B若发送数据,A仍要接受。也就是说,从B到A这个方向的连接并未关闭。这个状态可以会持续一些时间。
A收到B的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。
若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使用FIN=1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack=u+1。这是B就进入LAST-ACK状态,等待A的确认。
在A收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置为1,确认号ack=w+1,而自己的序号是seq=u+1(前面的FIN报文消耗了1个序号)。然后进入TIME-WAIT状态。请注意,现在TCP连接还没释放掉。必须再经过2MSL后,A才进入到CLOSED状态。MSL叫最长报文段寿命,一般为2分钟。
当B收到A发出的确认,就进入CLOSED状态。由此可见B结束TCP连接的时间要比A早一些。等到2MSL结束后A也进入CLOSED状态,至此完成了TCP四次挥手断开连接全过程。
当客户端最后一次发送消息时并没有直接进入close状态而是进入TIME_WAIT状态,这是因为TCP是面向连接的协议每一次发送都需要确认对方是否收到消息。客户端最后一次发送消息时可能会由于网络等其他原因导致服务器收不到消息,服务器就会选择从新给客户端发送一个FIN的包,如果客户端处于关闭状态将永远也收不到服务器发给它的消息了。
| 分类 | 分类描述 |
|---|---|
| 1** | 信息,服务器收到请求,需要请求者继续执行操作 |
| 2** | 成功,操作被成功接收并处理 |
| 3** | 重定向,需要进一步操作以完成请求 |
| 4** | 客户端错误,请求包含语法错误或无法完成请求 |
| 5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
1.GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?
name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。POST把提交的数据则放置在是HTTP包的包体中。
2."GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据
3,根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。
4,根据HTTP规范,POST表示可能修改变服务器上的资源的请求。
一、https协议需要到ca申请证书,一般免费证书很少,需要交费。
二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输
协议。
三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者
是443。
四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行
加密传输、身份认证的网络协议,比http协议安全。
1,当输入一个url后,浏览器开始工作,首先寻找浏览器缓存,系统缓存,路由器缓存,寻找是否缓存过这个url的信息,有缓存的话就直接使用缓存,没有的话就开始访问DNS服务器。
2,DNS服务器将域名解析成IP地址。
3,浏览器拥有IP后就可以找到服务器,然后在两者之间建立TCP连接,服务器和浏览器建立三次握手。
4,请求通过网络,服务器收到了请求,进行处理后,将需要的数据(http响应头)返回给浏览器。
5,浏览器收到http响应头,进行数据读取,进行浏览器渲染,解析html。
利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。
负责建立和管理节点之间的链路。该层的主要功能是:通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。
通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。
向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输。
向两个实体的表示层提供建立和使用连接的方法。将不同实体之间的表示层的连接称
为会话。
它对来自应用层的命令和数据进行解
释,对各种语法赋予相应的含义,并按照一定的格式传送给会话层。其主要功能是“处理用
户信息的表示问题,如编码、数据格式转换和加密解密”等。
它是计算机用户,以及各种应用程序
和网络之间的接口,其功能是直接向用户提供服务,完成用户希望在网络上完成的各种工
作。
1.使人们容易探讨和理解协议的许多细节。
2.在各层间标准化接口,允许不同的产品只提供各层功能的一部分,(如路由器在一到三层),或者只提供协议功能的一部分。
3. 创建更好集成的环境。
4. 减少复杂性,允许更容易编程改变或快速评估。
5.较低的层为较高的层提供服务。
6. 把复杂的网络划分成为更容易管理的层。
进程中线程同步的四种常用方式:
1、临界区
当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程想访问,则被挂起,直到拥有临近区的线程放弃临界区为止。
2、事件
事件机制中,允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。
3、互斥量
互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。
4、信号量
当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。
进程:是系统进行资源分配和调度的独立单位。
线程:是CPU调度和分派的基本单位。
区别:
1,一个程序至少有一个进程,一个进程至少有一个线程。
2,线程的划分尺度小于进程,使得多线程程序的并发性高。
3,另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
4,线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
5,从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
1,全局变量
进程中的线程间内存共享,这是比较常用的通信方式和交互方式。
2,Message消息机制
3,CEvent对象
线程在一定条件下,状态会发生变化。线程一共有以下几种状态:
1,新建状态(New)
2,就绪状态(Runnable)
3,运行状态(Running)
4,阻塞状态(Blocked)
(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒。
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
(3)、其他阻塞:运行的线程执行sleep()或join()方法时,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转变入就绪状态。
5,死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
自旋锁原理:当发生争用时,若Owner线程能在很短的时间内释放锁,则那些正在争用线程可以稍微等一等(自旋),在Owner线程释放锁后,争用线程可能会立即得到锁,从而避免了系统阻塞。基本思路就是自旋,不成功再阻塞,尽量降低阻塞的可能性,这对那些执行时间很短的代码块
来说有非 常重要的性能提高。
当进程执行过程中发生缺页中断时,需要进行页面换入,步骤如下:
1. 首先硬件陷入内核,在堆栈中保存程序计数器。大多数机器将当前指令的各种状态信息保存在CPU中特殊的寄存器中。
2. 启动一个汇编代码例程保存通用寄存器及其它易失性信息,以免被操作系统破坏。
3. 当操作系统发现是一个页面中断时,查找出来发生页面中断的虚拟页面(进程地址空间中的页面)。这个虚拟页面的信息通常会保存在一个硬件寄存器中,如果没有,操作系统必须检索程序计数器,取出该条指令,用软件分析该指令,通过分析找出发生页面中断的虚拟页面。
4. 检查虚拟地址的有效性及安全保护位。如果发生保护错误,则杀死该进程。
5. 操作系统查找一个空闲的页框(物理内存中的页面),如果没有空闲页框则需要通过页面置换算法找到一个需要换出的页框。
6. 如果找的页框中的内容被修改了,则需要将修改的内容保存到磁盘上,此时会引起一个写磁盘调用,发生上下文切换(在等待磁盘写的过程中让其它进程运行)。
7. 页框干净后,操作系统根据虚拟地址对应磁盘上的位置,将保持在磁盘上的页面内容复制到“干净”的页框中,此时会引起一个读磁盘调用,发生上下文切换。
8. 当磁盘中的页面内容全部装入页框后,向操作系统发送一个中断。操作系统更新内存中的页表项,将虚拟页面映射的页框号更新为写入的页框,并将页框标记为正常状态。
9. 恢复缺页中断发生前的状态,将程序指令器重新指向引起缺页中断的指令。
10. 调度引起页面中断的进程,操作系统返回汇编代码例程。
11. 汇编代码例程恢复现场,将之前保存在通用寄存器中的信息恢复。
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
详细说来就是,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
设置索引的代价:
1. 增加了数据库的存储空间;
2. 插入和修改数据时要花费较多的时间(因为索引随之变动)。
设置索引的好处:
1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性;
2. 可以大大加快数据的检索速度,这也是创建索引的最主要原因;
3. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义;
4. 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间;
5. 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
分析慢的原因:
记录慢查询日志,将查询慢的记入日志。
分析查询日志;
使用show profile set profiling=1
show profiles
show profile for query 临时表ID
shwo processlist 查看是否有大量线程处于不正常状态或者特征
使用explain分析单条语句。
1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2. 应尽量避免在 where子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
3. 应尽量避免在 where 子句中对字段进行 null值判断,否则将导致引擎放弃使用索引而进行全表扫描。
4. 应尽量避免在 where 子句中使用 or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。
5. in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t where num in(1,2,3)对于连续的数值,能用 between 就不要用 in。
6. 如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。
7. 应尽量避免在 where子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
8. 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
9. 不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10. 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
11. 不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:create table #t(...)
12. 多时候用 exists 代替 in 是一个好的选择。
13. 并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
14. 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
15. 应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
16. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
17. 尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
18. 任何地方都不要使用 select * from t,用具体的字段列表代替“*”,不要返回用不到的任何字段。
19. 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
20. 避免频繁创建和删除临时表,以减少系统表资源的消耗。
21. 临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
22. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替create table,避免造成大量 log,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
23. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table ,然后 drop table,这样可以避免系统表的较长时间锁定。
24. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
25. 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
26. 与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
27. 在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送DONE_IN_PROC 消息。
28. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
29. 尽量避免大事务操作,提高系统并发能力。
将一个命令的标准输出作为另一个命令的标准输入。也就是把几个命令组合起来使用,后一个命令使用前一个命令的结果。
例:grep -r "close" /home/* | more 在home目录下所有文件中查找,包括close的文 件,并分页输出。
vim三种模式:命令模式、插入模式、编辑模式。使用ESC或i或:来切换模式。
命令模式下:
:q 退出
:q! 强制退出
:wq 保存并退出
:set number 显示行号
:set nonumber 隐藏行号
/apache 在文档中查找apache 按n跳到下一个,shift+n上一个
yyp 复制光标所在行,并粘贴
h(左移一个字符←)、j(下一行↓)、k(上一行↑)、l(右移一个字符→)

LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。
Set不接受相同对象,使用Equals方法判断是否相同。
对于HashSet而言,它是基于HashMap实现的,底层采用HashMap来保存元素。
/*** @param e 将添加到此set中的元素。* @return 如果此set尚未包含指定元素,则返回true。*/public boolean add(E e) {return map.put(e, PRESENT)==null;}
如果此set中尚未包含指定元素,则添加指定元素。该段代码也可翻译为,如果此set没有包含满足(e==null?e2==null:e.equals(e2))的元素e2,则向此set添加指定的元素e。如果此set已包含该元素,则该调用不更改set并返回false。但底层实际将该元素作为key放入HashMap。
由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),新添加的Entry的value会将覆盖原来Entry的value(HashSet中的Value都是PRESENT),但key不会有任何改变,因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
TreeSet的作用是提供有序的Set集合,其基于TreeMap实现。
LinkedHashSet维护着一个运行于所有条目的双重链表列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。
LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同,因此LinkedHashSet的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数,调用父类的构造器,底层构造一个LinkedHashMap来实现,在相关操作上与父类HashSet的操作相同,直接调用父类HashSet的方法即可。
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初试化一个数组。这个数组为Entry数组。
Entry是一个static class,其中包含了key和value,也就是键值对,另外还包含了一个next的Entry指针。
put方法
/*** Associates the specified value with the specified key in this map.* If the map previously contained a mapping for the key, the old* value is replaced.** @param key key with which the specified value is to be associated* @param value value to be associated with the specified key* @return the previous value associated with <tt>key</tt>, or* <tt>null</tt> if there was no mapping for <tt>key</tt>.* (A <tt>null</tt> return can also indicate that the map* previously associated <tt>null</tt> with <tt>key</tt>.)*/public V put(K key, V value) {//其允许存放null的key和null的value,当其key为null时,调用putForNullKey方法,放入到table[0]的这个位置if (key == null)return putForNullKey(value);//通过调用hash方法对key进行哈希,得到哈希之后的数值。该方法实现可以通过看源码,其目的是为了尽可能的让键值对可以分不到不同的桶中int hash = hash(key);//根据上一步骤中求出的hash得到在数组中是索引iint i = indexFor(hash, table.length);//如果i处的Entry不为null,则通过其next指针不断遍历e元素的下一个元素。for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}
当我们put的时候,如果key存在了,那么新的value会代替旧的value,并且如果key存在的情况下,该方法返回的是旧的value,如果key不存在,那么返回null。
从上面的源代码中可以看出:当我们往 HashMap 中 put 元素的时候,先根据 key 的 hashCode 重新计算 hash 值,根据 hash 值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
hash(int h)
hash(int h)方法根据 key 的 hashCode 重新计算一次散列。此算法加入了高位计算,防止低位不变,高位变化时,造成的 hash 冲突。
final int hash(Object k) {int h = 0;if (useAltHashing) {if (k instanceof String) {return sun.misc.Hashing.stringHash32((String) k);}h = hashSeed;}//得到k的hashcode值h ^= k.hashCode();//进行计算h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}
对于任意给定的对象,只要它的hashCode()返回值相同,那么程序调用hash(int h)方法所计算得到的hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗比较大,在HashMap中:调用indexFor(int h,int length)方法来计算该对象应该保存在table数组的哪个索引处。
indexFor
/*** Returns index for hash code h.*/static int indexFor(int h, int length) {return h & (length-1);}
这个方法比较巧妙,它通过h&(table.length-1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。在HashMap构造器中有如下代码:
// Find a power of 2 >= initialCapacityint capacity = 1;while (capacity < initialCapacity)capacity <<= 1;
这段代码保证初试化时HashMap的容量总是2的n次方,当length总是2的n次方时,h&(length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
get
public V get(Object key) {if (key == null)return getForNullKey();Entry<K,V> entry = getEntry(key);return null == entry ? null : entry.getValue();}final Entry<K,V> getEntry(Object key) {int hash = (key == null) ? 0 : hash(key);for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;}return null;}
从上面的源代码中可以看出:从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位值的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。
LinkedHashMap实现与HashMap的不同之处在于,LinkedHashMap维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
LinkedHashMap 采用的 hash 算法和 HashMap 相同,但是它重新定义了数组中保存的元素 Entry,该 Entry 除了保存当前对象的引用外,还保存了其上一个元素 before 和下一个元素 after 的引用,从而在哈希表的基础上又构成了双向链接列表。
其实 LinkedHashMap 几乎和 HashMap 一样:从技术上来说,不同的是它定义了一个 Entry header,这个 header 不是放在 Table 里,它是额外独立出来的。LinkedHashMap 通过继承 hashMap 中的 Entry,并添加两个属性 Entry before,after,和 header 结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。
TreeMap的实现是红黑数算法的实现。
+ 红黑树简介
红黑树是一棵自平衡的排序二叉树。一棵基本的二叉排序树满足一个基本性质,即树中的任何节点的值大于它的左子节点,且小于它的右子节点。按照这个基本性质使得树的检索效率大大提高。而平衡二叉树为了阻止二叉树的失衡有一系列的算法,如:AVL,SBT,伸展树,TREAP,红黑树等。
平衡二叉树必须具备如下特性:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。也就是说该二叉树的任何一个等子节点,其左右子树的高度都相近。
红黑树顾名思义就是节点是红色或者黑色的平衡二叉树,它通过颜色的约束来维持着二叉树的平衡。对于一棵有效的红黑树二叉树而言我们必须增加如下规则:
1、每个节点都只能是红色或者黑色
2、根结点是黑色
3、每个叶节点是黑色的。
4、如果一个节点是红的,则它两个子节点都是黑的。也就是说在一条路径上不能出现相邻的两个红色节点。
5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
和HashMap一样,HashTable也是一个散列表,它存储的内容是键值对。
HashTable继承于Dictionary类,实现了Map,Cloneable,java.io.Serializable接口。其中Dictionary类是任何可将键映射到响应值的类的抽象父类,每个键和值都是对象。
LinkedBlockingDeque
LinkedTransferQueue
PriorityBlockingQueue
DelayQueue
ConcurrentLinkedDeque
ConcurrentSkipListMap
原子变量
随机数
newCachedThreadPool
newFixedThreadPool
newSingleThreadExecutor
Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作。Lock接口有3个实现它的类:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。lock必须被显式地创建、锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例化。
为了保证锁最终一定会被释放(可能会有异常发生),要把互斥区放在try语句块内,并在finally语句块中释放锁,尤其当有return语句时,return语句必须放在try字句中,以确保unlock()不会过早发生,从而将数据暴露给第二个任务。
首先获取当前线程
利用当前线程作为句柄获取一个ThreadLocalMap的对象
如果上述ThreadLocalMap对象不为空,则设置值,否则创建这个ThreadLocalMap对象并设置值
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
总结:实际上ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。
ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有)。而ThreadLocal的值其实也是被线程实例持有。
它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。

Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
其中通过一个配置文件描述Bean以及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IOC容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。
beanDefinition的定位:beandefinition为管理bean之间的依赖关系提供了帮助,只要遵循spring的定义规则来
提供bean定义信息,我们可以使用各种形式的bean定义信息,比较常用的是使用xml文件格式。在初始化ioc容器
的过程中,首先要定位到bean的定义信息,spring使用resource接口来统一了bean的定义信息,而定位则通过
resourceLoader来完成,如果使用上下文,applicationcontext由于本身是defaultresourceLoader的子类,
为用户提供了定位功能,如果是使用beanfactory作为ioc容器的话,客户需要为beanfactory制定响应的
resource来完成bean信息的定位。
容器初始化,如果使用上下文的话,需要一个对上下文进行初始化的过程,完成初始化以后,才能使用ioc容器
这个过程是通过构造函数的中调用refresh方法实现,refresh方法相当于就是容器的初始化函数,在初始化的
过程中,主要是要对beandefinition信息进行载入和注册工作,就是要在ioc容器当建立一个beandefinition定
义的数据映像,spring把载入功能从ioc容器中分离了出来,通过beandefinitionReader完成bean定义信息的读
取,解析和ioc容器内部beandefinition的建立,在defaultListablebeanfactory中,beandefintion被维护在
hashmap中,ioc容器的bean管理和操作就是通过beandefinition来完成。
在容器初始化完成之后,但初始化只是在ioc容器内部建立了beandefinition,具体的依赖关系还没有注入,
容器在用户第一次向ioc容器请求bean时,ioc容器对相关的bean依赖进行注入,如果需要提前注入,可以通过
lazy-init属性进行预初始化(也是上下文初始化的一部分,起到提前完成依赖注入的控制作用),在依赖注入
完成之后,ioc就会保持这些具备依赖关系的bean供用户直接使用,通过getbean来获得bean,
依赖注入主要通过createbeanInstance和populatebean这两个方法,
createbeanInstance主要是根据beandefintion来生成
bean所包含的对象,可以通过工厂方法、构造函数或者autowire进行实例化。构造函数有两种实例化策略,
一是通过beanUtils,利用jvm的反射功能,二是利用gclib来生成。
在初始化bean对象之后,就需要把依赖关系设置好,主要是对bean对象的属性的处理过程。
这个过程是在populatebean中委托beandefinitionRResolver对beandefinition进行解析,通过beanwrapper
的setProertyValues方法然后注入到property中。
在bean的创建和对象依赖注入中,需要通过依据beandefinition中的信息来递归的完成依赖注入,一个是
在上下文体系当中查找需要的bean和创建bean的递归调用。另一个就是在依赖注入的的时候,递归的调用
容器的getbean方法,得到当前Bean的依赖bean,同时触发了对依赖bean的创建和注入。在对bean的属性
属性进行依赖注入的是偶,解析的过程也是递归的过程。
JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。
在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内
部处理逻辑的InvocationHandler接口的实现。当使用者调用了代理对象所代理的接口中的方法的时候, 这个调用的信息会被传递给InvocationHandler的invoke方法。在invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者
也可以使用第三方类生成器gclib来完成
| ' | MyBatis | Hibernate |
|---|---|---|
| 开发方面 | 半自动化,sql需要手工完成,稍微繁琐 | sql语句已经被封装,直接可以使用,加快系统开发 |
| sql优化方面 | 手动编写sql,可以避免不需要的查询,提高系统性能 | 自动生成sql,有些语句较为繁琐,会多消耗一些性能 |
| 对象管理对比 | 自行管理映射关系 | 完整的对象映射框架,开发过程中,无需过多关注底层实现,只要去管理对象即可 |
| 缓存方面 | 二级缓存配置是在每个具体的表-对象映射中进行详细配置,并且MyBatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现 | 在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是哪种缓存 |
| Mybatis在使用的时候需要谨慎一点 | Hibernate具有良好的管理机制,用户不需要关注SQL,如果二级缓存出现脏数据,系统会保存 |
http://blog.csdn.net/u013322876/article/details/78953360
redisRedis 数据类型:String、List、Set、Sorted Set、HashString二进制安全的字符串set key valueget keyList双向链表结构可以用来实现消息队列,先用rpush放入队尾,在用lpop从对头取出。Set无序集合内部通过HashTable实现,查找和删除的时间复杂度为O(1)。其数据类型的优点是快速查找元素是否存在,用于记录一些不能重复的数据。Sorted Set有序集合Sorted Set通过一个double类型的整数score进行排序。其通过SkipLIst(跳跃表)和HashTable组合完成。SkipList负责排序,HashTable负责保存数据。可以使用Sorted Set构建一个具有优先级的队列。Hash HashTableHash类型是每个Key对应一个HashTable,添加、删除和修改的时间复杂度都是O(1)。Hash类型适合应用于存储对象,例如用户信息对象。把用户Id作为key,可把用户信息保存到Hash类型中。新建一个Hash类型对象时,为了节省内存,Redis使用zipmap存储数据。这个zipmap并不是真正的HashTable,添加、删除和修改操作的时间复杂度都是O(n),相比普通的HashTable,zipmap节省不少内存。如果表中的field或者value大小超过一定限制,Redis在内部自动将zipmap替换成正常的HashTable存储。修改配置文件的hash-max-zipmap-entries和hash-max-zipmapvalue选项设置这两个限制。持久化:内存快照:将内存中的数据以快照的方式写入二进制文件中。Redis每隔一段时间进行一次内存快照操作,客户端使用save或者bgsave命令告诉Redis需要做一次内存快照操作。save命令在主线程中保存内存快照,Redis由单线程处理所有请求,执行save命令可能阻塞其他客户端请求,从而导致不能快速相应请求,所以建议不使用save命令。内存快照不是只写入增量数据,所以如果写入数据量大,写入操作比较频繁,会严重影响性能。save <seconds> <changes>日志追加:把增加、修改的命令通过write函数追加到文件尾部(appendonly.aof)
git-flow 并不是要替代 Git,它仅仅是非、常聪明有效地把标准的 Git 命令用脚本组合了起来。
feature
git-flow会在git flow feature finish命令后进行清理操作。它会删除这个当下已经完成的功能分支,并且换到“develop”分支。
当你认为现在在 “develop” 分支的代码已经是一个成熟的 release 版本时,这意味着:第一,它包括所有新的功能和必要的修复;第二,它已经被彻底的测试过了。如果上述两点都满足,那就是时候开始生成一个新的 release 了:
git flow release finish 1.1.5
这个命令会完成如下一系列的操作:
1.首先,git-flow会拉取远程仓库,以确保目前是最新的版本。
2.然后,release的内容会被合并到“master”和“develop”两个分支中去,这样不仅产品代码为最新的版本,而且新的功能分支也将基于最新代码。
3.为便于识别和做历史参考,release提交会被标记上这个release的名字。
4.清理操作,版本分支会被删除,并且回到“develop”。
当发现release版本出错,使用“hotfix”工作流程。因为这是对产品代码进行修复,所以这个hotfix是基于“master”分支。
这也是和release分支最明显的区别,release分支都是基于“develop”分支的。因为你不应该在一个还不完全稳定的开发分支上对产品代码进行修复。
完成hotfixes的个过程非常类似于发布一个release版本:
1,完成的改动会被合并到“master”中,同样也会合并到“develop”分支中,这样就可以确保这个错误不会再出现在下一个release中。
2,这个hotfix程序将被标记起来以便于参考。
3,这个hotfix分支将被删除,然后切换到“develop”分支上去。
git-flow 并不会为 Git 扩展任何新的功能,它仅仅使用了脚本来捆绑了一系列 Git 命令来完成一些特定的工作流程。
其次,定义一个固定的工作流程会使得团队协作更加简单容易。无论是一个 “版本控制的新手” 还是 “Git 专家”,每一个人都知道如何来正确地完成某个任务。