最后
现在其实从大厂招聘需求可见,在招聘要求上有高并发经验优先,包括很多朋友之前都是做传统行业或者外包项目,一直在小公司,技术搞的比较简单,没有怎么搞过分布式系统,但是现在互联网公司一般都是做分布式系统。
所以说,如果你想进大厂,想脱离传统行业,这些技术知识都是你必备的,下面自己手打了一份Java并发体系思维导图,希望对你有所帮助。
多线程文章回顾:
之前在写多线程文章的时候,简单说了一下synchronized锁在jdk1.6以后会有各种的优化:适应自旋锁,锁消除,锁粗化,轻量级锁,偏向锁。
本以为这些优化是非常难以理解的东西,其实不然~~~简单了解一下还是很好理解的。
2.1适应自旋锁
锁竞争是kernal mode下的,会经过user mode(用户态)到kernal mode(内核态) 的切换,是比较花时间的。
自旋锁出现的原因是人们发现大多数时候锁的占用只会持续很短的时间,甚至低于切换到kernal mode所花的时间,所以在进入kernal mode前让线程等待有限的时间,如果在此时间内能够获取到锁就避免了很多无谓的时间,若不能则再进入kernal mode竞争锁。
在JDK 1.6中引入了自适应的自旋锁,说明自旋的时间不固定,要不要自旋变得越来越聪明。
自旋锁在JDK1.4.2中就已经引入,只不过默认是关闭的,可以使用-XX:+UseSpinning
参数来开启,在JDK1.6中就已经改为默认开启了。
参考资料:
- 自旋锁和使线程休眠的非自旋锁各有什么适用场景?https://www.zhihu.com/question/38857029/answer/78480263
2.2锁消除
如果JVM明显检测到某段代码是线程安全的(言外之意:无锁也是安全的),JVM会安全地原有的锁消除掉!
比如说:
public void vectorTest(){
Vector vector = new Vector();
for(int i = 0 ; i < 10 ; i++){
vector.add(i + “”);
}
System.out.println(vector);
}
Vector是默认加锁的,但JVM如果发现vector变量仅仅在vectorTest()
方法中使用,那该vector是线程安全的。JVM会把vector内部加的锁去除,这个优化就叫做:锁消除。
2.3锁粗化
默认情况下,总是推荐将同步块的作用范围限制得尽量小。
但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,频繁地进行互斥同步操作也会导致不必要的性能损耗。
JVM会将加锁的范围扩展(粗化),这就叫做锁粗化。
2.4轻量级锁
轻量级锁能提升程序同步性能的依据是**“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”**,这是一个经验数据。
-
如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销
-
但如果存在锁竞争,除了互斥量的开销外,还额外发生了CAS操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。
简单来说:如果发现同步周期内都是不存在竞争,JVM会使用CAS操作来替代操作系统互斥量。这个优化就被叫做轻量级锁。
2.5偏向锁
偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了!
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数
-XX:-UseBiasedLocking
来禁止偏向锁优化反而可以提升性能。
2.6简单总结各种锁优化
-
自适应偏向锁:自旋时间不固定
-
锁消除:如果发现代码是线程安全的,将锁去掉
-
锁粗化:加锁范围过小(重复加锁),将加锁的范围扩展
-
轻量级锁:在无竞争的情况下使用CAS操作去消除同步使用的互斥量
-
偏向锁:在无竞争环境下,把整个同步都消除,CAS也不做。
参考资料:
三、TCP粘包,拆包
==========
这是在看wangjingxin大佬面经的时候看到的面试题,之前对TCP粘包,拆包没什么概念,于是就简单去了解一下。
3.1什么是拆包粘包?为什么会出现?
在进行Java NIO学习时,可能会发现:如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况。
TCP的首部格式:
-
TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;
-
从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段
基于上面两点,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。
一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包
接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包
拆包和粘包的问题导致接收端在处理的时候会非常困难(因为无法区分一个完整的数据包)
3.2解决拆包和粘包
分包机制一般有两个通用的解决方法:
-
1,特殊字符控制
-
2,在包头首都添加数据包的长度
如果使用netty的话,就有专门的编码器和解码器解决拆包和粘包问题了。
tips:UDP没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是UDP报文或用户数据报,发送的时候既不合并,也不拆分。
参考资料
-
https://blog.youkuaiyun.com/scythe666/article/details/51996268—>TCP粘包,拆包及解决方法
-
http://www.ideawu.net/blog/archives/993.html—>关于TCP粘包和拆包的终极解答
四、select、poll、epoll简单区别
=======================
NIO回顾:
在Linux下它是这样子实现I/O复用模型的:
调用select/poll/epoll
其中一个函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。
这几个函数是有些区别的,可能有的面试官会问到这三个函数究竟有什么区别:
区别如下图:
两句话总结:
-
select和poll
都需要轮询每个文件描述符,epoll
基于事件驱动,不用轮询 -
select和poll
每次都需要拷贝文件描述符,epoll
不用 -
select
最大连接数受限,epoll和poll
最大连接数不受限
tips:epoll在内核中的实现,用红黑树管理事件块
4.1通俗例子
现在3y在公司里边实习,写完的代码需要给测试测一遍。
select/poll
情况:
- 开发在写代码,此时测试挨个问所有开发者,你写好程序了没有?要测试吗?
epoll
情况:
- 开发写完代码了,告诉测试:“我写好代码了,你去测测,功能是XXX”。于是测试高高兴兴去找bug了。
其他通俗描述[1]:
一个酒吧服务员(一个线程),前面趴了一群醉汉,突然一个吼一声“倒酒”(事件),你小跑过去给他倒一杯,然后随他去吧,突然又一个要倒酒,你又过去倒上,就这样一个服务员服务好多人,有时没人喝酒,服务员处于空闲状态,可以干点别的玩玩手机。至于epoll与select,poll的区别在于后两者的场景中醉汉不说话,你要挨个问要不要酒,没时间玩手机了。io多路复用大概就是指这几个醉汉共用一个服务员。
来源:
其他通俗描述[2]:
简单举个例子(可能也不是很形象)select/poll饭店服务员(内核)告诉饭店老板(用户程序):”现在有客人结账“但是这个服务员没人明确告诉老板,哪几桌的客人结帐。老板得自儿一个一个桌子去问:请问是你要结帐?epoll饭店服务员(内核)告诉饭店老板(用户程序):”1,2,5号客人结账“老板就可以直接去1,2,5号桌收钱了
来源:
深入了解参考资料:
- https://www.cnblogs.com/Anker/p/3265058.html—>select、poll、epoll之间的区别总结[整理]
五、Java内存模型
==========
JVM博文回顾:
之前在写JVM的时候,还一度把JVM内存结构与Java内存模型给搞混了~~~还好有热心的网友给我指出来。
JVM内存结构:
Java内存模型:
操作变量时的规则:
-
Java内存模型规定了所有的变量都存储在主内存
-
线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝
-
线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量
从工作内存同步回主内存实现是通过以下的8种操作来完成:
-
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
-
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
-
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
-
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
-
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
-
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
-
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
-
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的
保证原子性的操作:
-
read、load、assign、use、store和write
-
synchronized锁
最后
一次偶然,从朋友那里得到一份“java高分面试指南”,里面涵盖了25个分类的面试题以及详细的解析:JavaOOP、Java集合/泛型、Java中的IO与NIO、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、Memcached、MongoDB、Spring、Spring Boot、Spring Cloud、RabbitMQ、Dubbo 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。
这不,马上就要到招聘季了,很多朋友又开始准备“金三银四”的春招啦,那我想这份“java高分面试指南”应该起到不小的作用,所以今天想给大家分享一下。
请注意:关于这份“java高分面试指南”,每一个方向专题(25个)的题目这里几乎都会列举,在不看答案的情况下,大家可以自行测试一下水平 且由于篇幅原因,这边无法展示所有完整的答案解析
o 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。
这不,马上就要到招聘季了,很多朋友又开始准备“金三银四”的春招啦,那我想这份“java高分面试指南”应该起到不小的作用,所以今天想给大家分享一下。
[外链图片转存中…(img-mifSDV8m-1715595395081)]
请注意:关于这份“java高分面试指南”,每一个方向专题(25个)的题目这里几乎都会列举,在不看答案的情况下,大家可以自行测试一下水平 且由于篇幅原因,这边无法展示所有完整的答案解析