Java开发3年应该掌握的小知识(中)
高并发
1.造成线程安全的主要因素有哪些?
线程安全性:当多个线程访问某个类时,不管运行时环境采用任何调度方式或者这些进程将如何交替执行,而且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
线程安全性主要体现三个方面:
原子性:提供了互斥访问,同一时刻只能有一个线程进行操作。
可见性:一个线程对主内存的修改可以及时被其他线程观察到。
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重新排序的存在,该观察结果是杂乱无章的。
2.互斥锁是什么?java锁如何分类?
由于并发状态下,操作系统对多个进程进行调度,而多个进程可能都会操作硬件的需求,这时就会产生多个进程对资源的共享,而共享就意味着竞争,竞争会产生很多问题。这样就需要一种机制或者手法去了解竞争,是竞争变得有序化,从而使共享资源按照预定的结果去获取。这种手段就是互斥锁。
使用互斥锁会由并发改为串行,牺牲了运行效率,但保证了数据安全。
锁的分类:乐观锁|悲观锁,独享锁|共享锁,互斥锁|读写锁,分段锁、自旋锁,偏向锁|轻量级锁|重量级锁等
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
悲观锁:认为对于同一个数据的并发操作,一定会发生修改的,哪怕没有修改,也会认为修改。因此对于同一份数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁并发操作一定会出问题。
独享锁:独享锁是指该锁一次只能被一个线程所持有。
共享锁:共享锁是指该锁可被多个线程所持有。
互斥锁:在Java中的具体实现就是ReentrantLock。
读写锁:在Java中的具体实现就是ReadWriteLock。
分段锁:是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
自旋锁:指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁:是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁:是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
3.线程的创建方式?
①继承Thread类创建线程。
②实现Runnable接口创建线程。
③使用Callable和Future创建线程。
④使用线程池例如用Executor框架。
4.三种创建方式分别有什么区别?
继承Thread类
子类继承Thread类具备多线程能力
启动线程:创建子类实例,直接调用父类Thread的start方法
缺点:OOP单继承的局限性
实现Runnable接口
实现Runnable接口具备多线程能力
创建线程:向Thread对象传入目标对象,通过Thread对象调用start方法
优点:避免了OOP单继承的局限性,灵活方便,方便同一个对象被多个线程使用()
实现Callable接口
实现Callable接口具备多线程能力
启动线程:需要创建执行服务,提交任务执行,最后还需要关闭服务。(启动线程的方式不止这一种)
优点:可以获取执行结果;声明式的抛出异常,减少了了某些情况下的程序开发复杂度;
5.线程池的核心参数是哪些?
a.核心线程数
b 最大线程数
c 线程空闲时间
d 阻塞队列大小:queueCapacity
e 任务拒绝处理器 :rejectedExceptionHandler
6.通过线程池创建线程的流程是什么?
7.Lock接口
Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock。
8.synchronize和volitile的区别?
1)volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。synchronized则是锁定当前变量,只有当前线程可以访问该变量,其它线程被阻塞。
2)volatile仅能使用在变量级别,synchronized则可以使用在变量、方法。
3)volatile仅能实现变量修改的可见性,而synchronized则可以保证变量修改的可见性和原子性。《Java编程思想》上说,定义long或double时,如果使用volatile关键字(简单的赋值与返回操作),就会获得原子性。(常规状态下,这两个变量由于其长度,其操作不是原子的)
4)volatile不会造成线程阻塞,synchronized会造成线程阻塞。
5)使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域。
9.设计一个高并发的网站通用的技术有哪些?
10.CAS是什么?
CAS的全称是Compare And Swap——比较交换,CAS中有三个核心参数:
主内存中存放的V值,所有线程共享。
线程上次从内存中读取的V值A存放在线程的帧栈中,每个线程私有。
需要写入内存中并改写V值的B值。也就是线程对A值操作后放入到主存V中。
分布式
1.关于分布式的基本概念?
可靠性、可扩展性、可维护性。
2.SpringBoot的优势?
①遵循"习惯优于配置"原则,使用Spirng Boot只需很少的配置,大部分时候可以使用默认配置。
②项目快速搭建,另外还可以无配置整合第三方框架。
③可完全不使用xml配置,只是用自动配置和Java Config。
④内嵌Servlet如Tomcat容器,应用可用jar包执行(java-jar)。
⑤运行中应用状态的监控。
3.springboot自动配置的原理是什么?
4.如果自己集成一个jar包到springboot中,该如何做?
5.如何解决分布式事务的问题?
XA 方案:两阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok,那么就正式提交事务,在各个数据库上执行操作;如果任何其中一个数据库回答不 ok,那么就回滚事务。
TCC 方案:全称是:Try、Confirm、Cancel。
Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留。
Confirm 阶段:这个阶段说的是在各个服务中执行实际的操作。
Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大。
本地消息表:这个方案最大的问题就在于严重依赖于数据库的消息表来管理事务啥的,如果是高并发场景不好处理,不易扩展,一般很少用。
可靠消息最终一致性方案:干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务
最大努力通知方案:系统 A 本地事务执行完之后,发送个消息到 MQ;
这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ 然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统 B 的接口;
要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。
6.你们公司是如何处理分布式事务的?
我们某某特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。因为 TCC 方案如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。
web
1.servlet的实现以及其中的方法?
①实现servlet接口
②继承GeneriServlet接口
③继承httpServlet类: 对Http协议的一种封装。
2.token生成的原理?
原理:用户第一次登录,服务器通过数据库校验其UserId和Password合法,则再根据
随机数字+userid+当前时间戳 再经过DES加密生成一个token串。
作用:请求参数中带token
①用户在调用需要登录操作的接口时,无需传递userid和password即可完成操作(因为token代表登录成功)
②服务器控制过期时间,假如一个极端情况,服务器端的token规则泄露,则可以控制用户可以重新登录,获取新的token
关于前端
1.闭包的理解?
闭包是指有权访问另一个函数作用域中的变量的函数。 --《JavaScript高级程序设计》
函数对象可以通过作用域关联起来,函数体内的变量都可以保存在函数作用域内,这在计算机科学文献中称为“闭包”,所有的javascirpt函数都是闭包。 --《Javascript权威指南》
在函数a执行完并返回后,闭包使得JavaScript的垃圾回收机制不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量,闭包需要循序渐进的过程。
闭包由俩个部分构成:函数、以及创建该函数的环境。
应用场景:
保护函数内的变量安全。函数a中只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
在内存中维持一个变量。
2.js函数的理解?
①函数关键字:function
②函数定义是由,关键字,函数名加一组参数,大括号中需要执行的一段代码定义。
③函数名:必选,在同一个页面中,函数名是唯一的,而且会区分大小写。
④参数:可选,用于指定参数列表。
function 函数名([参数1,参数2,]){
函数语句体;
[ return 表达式;]
}
3.如何优化前端?
①减少Http请求
②将脚本放在底部
③避免css表达式
④使用外部的JavaScript和CSS
⑤减少DNS查找