51.如何跳出多重循环
// break,内层循环想要跳出外层循环
int i=0;
int j=0;
a: for(;i<10;i++){
b:for(;j<10;j++){
if(j==4){
break a; //跳出a所在的循环
}
52.使用spring的好处?
//能够实现依赖注入和控制反转;
//由Spring自动管理bean的生命周期;
//减少配置文件的数量,使得配置信息一元化;
//Spring支持声明式事务;
//降低各组件的耦合,实现软件各个层之间的解耦;
//方便异常的处理
53.error和Execption的区别?
都继承自Throwable
error是虚拟机层面抛出的异常,无法被改变和解决,exception是程序可以处理的异常,这个异常又分为编译时异常,和运行时异常;
54.事务的特性?
//ACID 原子性 ,一致性,隔离性, 持久性
55.Java程序的执行顺序
加载main所在类字节码信息(对静态属性初始化并分配内存空间,加载静态方法), 找到程序执行的入口 main方法,然后当定义了类的对象时,调用非静态属性(变量),构造块,普通方法声明。最后对象初始化调用类的构造函数
56.Mybatis的动态sql语法
if ,choose when otherwise ,set ,trim
<<-----动态sql传参数----->>
57.什么是DI,有哪些方式?什么是IOC?
依赖注入,
setter注入,构造注入,p命名空间注入
控制反转—>>以前创建对象需要new,现在不用了,spring自动在底层通过反射+无参构造去创建一个对象;
58.springMVC的原理?
请求发送到前端控制器,由前端控制器找到处理器并将请求发送给处理器,处理器处理完请求后返回给前端控制器,由前端控制器负责返回view,并封装为http,然后由vie相应页面的http
59.SpringAOP的原理?
//切面 动态代理方法的增强
60. Java支持多继承吗?有哪些方式进行实现?
理论上不支持,但是通过内部类可以打破这个限制;
可以转变为多实现;
61.什么是反射,反射的原理?
反射是指在运行时获得类的一些属性,方法构造等信息,
原理时 字节码信息加载到jvm时会创建一个class对象,通过class对象去获得字节码中的的一些信息;
62.Http请求状态码?及其含义
404 未找到
302 重定向
200 Ok
500 服务器错误
63.什么是泛型,泛型的好处?
泛型其实是一种参数化类型,也就是说暂时抹去了类型,jit在编译时会进行检查
类型安全
泛化代码使得代码高可用;
性能比较高
64.springboot优缺点
优点:
通过整合依赖(starter)简化了依赖加载;
内嵌tomcat/jetty,可快速进行项目开发;
快速构建项目。
对主流开发框架的无配置集成。
可提供运行时的应用监控。
与云计算的天然集成(微服务)
缺点:
版本迭代快,不同版本之间存在不兼容的情况,升级起来就比较不方便;
65.freemarker中的常用标签
<#if userage > age>
${name}比${username}大
<#else>
${name}比${username}小
</#if>
</br>
That is free-marker-demo
${GoodDate?string("yyyy-MM-dd")}<#--指定一个模板-->
${GoodDate?date_if_unknown}<#--明确时间类型-->
${GoodDate?datetime_if_unknown}
<#list ["apple", "banana", "orange"] as fruit>
${fruit}
</#list>
更多信息—>>>>
66. 什么nginx的反向代理?
以代理服务器来接受连接请求,然后将请求转发给内部网络的服务器,代理对象将结果返回给客户端;
正向代理是位于客户端,代理客户端发出请求---->>常常用于科学上网
反向代理位与服务器端,客户端访问的是客户端的代理服务器,------目的是负载均衡
67.git的常用命令和作用?
常用的:
初始化仓库 git init
将内容存到暂存区 git add --all
将内容提交到仓库 git commit -m -all
查看仓库状态 git status
克隆仓库 git clone +ip
查看提交历史 git log
查看提交历史–详细 git reflog
git reset --hard 7a4b2dc
git仓库保证数据完整性的原理----根据提交的数据计算得到一个hash值,作为数据额索引,回退或者升级时候通过移动指针来回退或者升级到相应的版本;
git reset --hard 与git reset --mixed 和git reset --soft 的区别
git reset --hard
当git仓库中指针移动时重置暂存区,工作区,即回退/升级的时候暂存区的版本内容根工作区的版本内容与git仓库中的保持一致;
白话版---->>>
硬恢复,即git仓库与暂存区一致,刚刚还未从暂存区提交到git仓库的会被清空,工作区也是;;
git reset --mixed
当git仓库中指针移动时重置暂存区,工作区的内容不会受到影响
白话版—>>>
混合恢复,即git仓库与暂存区一直,工作区不受影响;
git reset --soft
当git仓库中指针移动时暂存区,跟工作区的内容不会受到影响
拉取仓库 git fetch +ip
git merge 主分支
合并前可能会先拉取在修改最后内容一致后才开始合并;
创建分支git branch name
切换分支 git checkout 分支名
更多命令---->>
68.负载均衡的策略?
顺序轮询
随机轮询
指定权重
IP绑定
响应速度均衡
69.为什么 Java 中只有值传递
按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用 (call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以 修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。
这其实跟底层设计有关,在线程拿到一个数据时都会拿到该数据的一个拷贝而不是数据本身,这也就侧面印证了violate关键字;
70.ThreadLocal有什么作用?
数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的
Spring框架里面就是用的ThreadLocal来实现这种隔离
spring中原码
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
Spring采用Threadlocal的方式,来保证单个线程中的数据库操作使用的是同一个数据库连接,即使一个修改了,也不影响其他;
71.什么是springboot,约定大于配置
springboot有默认的配置,但是当用户自定义配置时,会优先采用用户的配置;
72.mysql的左右连结有什么区别?
左连接是左表全部显示,where只影响右表,
右连接反之;
73.在一个静态方法内调用一个非静态成员为什么是非法的?
因为静态资源随着类字节码一起加载,但此时可能非静态资源还没有被加载,也就不能调用;
由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。
74.finally语句块块中的代码一定会被执行吗?
不一定,
1,当finally中的与菊花发生了异常;
2,在前面的代码中 使用了System.exit()退出程序;
3,程序所在的线程被杀死了
4,cpu被关闭;
75.获取用键盘输入常用的两种方法
public class ReviewDemo {
public static void main(String[] args) {
Scanner scanner= new Scanner(System.in);
String next = scanner.next();
System.out.println(next);
BufferedReader bufferedReader= new BufferedReader(new InputStreamReader(System.in));
try {
String s = bufferedReader.readLine();
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bufferedReader.close();
scanner.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
76.HashMap和HashTable的区别
比较标准的答案---->>
1,两者父类不同,
HashMap继承自AbstractMap类,而HashTable继承自Dictionary类但他们都实现了了map、Cloneable(可复制)、Serializable(可序列化)接口;
2,方法上有一些不同由于hashtable继承自Dictionary,所以多提供了elments() 和contains() 两个方法。
3,hashmap的key支持一个null,而hashtable key value都不支持为null;
4,初始容量不同,hashtable初始为11,hashmap初始为16,
5,安全性不同,hashmap在多线程下是不安全的,而hashtable是安全的,但是hashmap在单线程下效率高,多线程下可以用ConcurrentHashMap,它的效率比Hashtable要高好多倍。因为
ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
77.Java中的四种引用
强引用:
强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:
String str=“hello”
软引用:
软引用在程序内存不足时,会被回收,使用方式:
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
//这里软引用是指的这个指向str的引用,而wrf这个SoftReference对象也是一个强引用;
可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的
对象。
弱引用:
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference<String> wrf = new WeakReference<String>(new String("str"))
Java源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用;
虚引用:
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入 ReferenceQueue 中。其它引用是被JVM回收后才被传入 ReferenceQueue 中的。由于这个机制,所以虚引用大多被
用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有 ReferenceQueue ;
可用场景: 对象销毁前的一些操作;
package com.gavin;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
/**
* @Description: UP up UP
* @author: Gavin
* @date:2022/5/14 22:04
*/
public class RefferenceDemo {
public static void main(String[] args) throws InterruptedException {
// 强引用 ---内存不够时也不会被回收,只会抛异常
String str= new String("hello");
System.out.println(str);
String str2= "world";
// 软引用
SoftReference<String> softReference= new SoftReference<>(str2);
System.gc();//垃圾回收
System.out.println(softReference.get());
String str3= "hello_world";
//弱引用
WeakReference<String> weakReference= new WeakReference<>(str3);
System.gc();
System.out.println(weakReference.get());//有时候
String str4= "hello你好";
// 虚引用
PhantomReference<String>phantomReference= new PhantomReference<>(str4,new ReferenceQueue<>());
System.gc();
System.out.println(phantomReference.get());
}
}
78.实现Java反射的类:
1)Class:表示正在运行的Java应用程序中的类和接口
注意: 所有获取对象的信息都需要Class类来实现。
2)Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
3)Constructor:提供关于类的单个构造方法的信息以及它的访问权限
4)Method:提供类或接口中某个方法的信息
79.给出“生产者-消费者”问题的一种解决方案
常见的解决方案是加锁来实现的,
1,借助object类中的wait()与notice()方法,
2,开辟生产者队列,消费者队列,借助锁去实现
3,直接使用阻塞队列来实现;
package com.gavin;
import java.util.concurrent.ArrayBlockingQueue;
public class BlockingQueueTest {
private int size =20;
private ArrayBlockingQueue queue= new ArrayBlockingQueue(size);
public static void main(String[] args) {
BlockingQueueTest test = new BlockingQueueTest();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
while(true){
try{
queue.take();
System.out.println("队列剩余元素---"+queue.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
while(true){
try{
queue.put(1);
System.out.println("队列剩余空间---"+(size-queue.size()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
80,ArrayBlockingQueue, CountDownLatch 类的作用
ArrayBlockingQueue----有界阻塞队列,满队列中put会阻塞,知道有线程take,空队列中take也会阻塞,直到有线程put
CountDownLatch -----允许线程等待,直到计数器为0,应用场景-----一件事情的结果之前必须要走的流程,比如 向上级申请某权限,必须要走部门主管,部门经理,总经理,等;
81.java8新特性:
- 1.Lambda表达式与Functional接口
- 2.接口的默认方法与静态方法
- 3.optional 与stream
82.简述java的运行机制
.java文件通过编译器生成class文件,class文件经过累加器加载到jvm里,jvm解析class文件为机器码,最后程序运行起来了;
每一种平台的解析器时不同的,但是实现的虚拟机是相同的,所以java可以做到跨平台;
83.jvm后台运行的系统线程有哪些?
- 1,虚拟机线程
这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-the- world 垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。
- 2.周期性线程
负责定时器事件(也就是中断),用来调度周期性操作的执行;
- 3.gc线程
支持垃圾回收活动;
- 4,编译器线程
在运行时将字节码动态编译成本地平台相关的机器码;
- 5.信号分发线程
接收发送到 JVM 的信号并调用适当的 JVM 方法处理。
84.JVM运行时内存划分
85.复制算法GC回收过程
首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,则复制到老年代区),同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区);
86.垃圾回收算法
引用计数----注意循环引用的问题
在 Java 中,引用和对象是有关联的。如果要操作对象则必须用引用进行。因此,很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说,即一个对象如果没有任何与之关联的引用,即他们的引用计数都不为 0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。
可达性分析
为了解决引用计数法的循环引用问题,Java 使用了可达性分析的方法。通过一系列的“GC roots” 对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
- 标记清除算法
标记,然后清除
缺点:碎片化严重
- 复制算法
按内存容量将内存划分为等大小的两块。每次只使用其中一块,当这一块内存满后将尚存活的对象复制到另一块上去,把已使用的内存清掉
缺点:压缩了内存使用大小
- 3.标记整理算法
结合了以上两个算法,为了避免缺陷而提出。标记阶段和 Mark-Sweep 算法相同,标记后不是清理对象,而是将存活对象移向内存的一端。然后清除端边界外的对象;
缺点:当垃圾很耗费系统性能;
现代的gc算法综合了以上几种算法
- 4.分代收集法
分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将 GC 堆划分为老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收时都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。
对于新生代采用的复制算法,老年代采用标记整理法
分带收集算法过程简述:
- JAVA 虚拟机提到过的处于方法区的永生代(Permanet Generation),它用来存储 class 类,常量,方法描述等。对永生代的回收主要包括废弃常量和无用的类。
- 对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目前存放对象的那一块),少数情况会直接分配到老生代。
- 当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From Space 进行清理。
- 如果 To Space 无法足够存储某个对象,则将这个对象存储到老生代。
- 在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。
- 当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被移到老生代中。
Java 堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法年老代主要使用标记-整理垃圾回收算法,因此 java 虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器,
87.负载均衡(dubbo,nginx,eureka)
dubbo的负载均衡策略
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。
具体实现上,Dubbo 提供的是客户端负载均衡,即由 Consumer 通过负载均衡算法得出需要将请求提交到哪个 Provider 实例。
1,RandomLoadBalance—>>加权随机
按权重设置随机概率,在一个截面上碰撞的概率很高,但调用量越大,分布的越均匀,而且按照概率使用权重后也比较均匀,有利于动态调整提供者权重;
缺点:存在慢的累计请求问题,比如某台机器很慢,但是没有宕机,当请求调到该台机器上时就卡在哪里了,久而久之所有的请求都卡在该台机器上;
2,RandomRibonLoadBalance—>>加权轮询(借助于nginx的平滑加权轮询)
按照权重设置的轮询比率循环调用节点;
缺点:存在慢的累积请求问题;
但是dubbo在原始算法上做了改进,借鉴nginx的平滑加权算法,使得过程中节点流量是平滑的,且在短周期内概率都是按照期望分布的.
3,LeastActiveloadBalance ---->>最少活跃优先+加权随机
活跃数越低,越优先调用,相同的活跃数的进行加权随机,活跃数是指调用前后计数差,数值越低表示提供者的处理能力越强;而慢的提供者会因此收到更少请求,毕竟能者多得呀!!
4,ShortestResponseLoadbalance---->>最短响应优先
在最近一个滑动窗口中,响应时间越短,越优先调用,相同时间的进行加权随机;
即响应时间越快的的提供者会收到更多的处理请求;
缺点:可能造成流量过于集中于高性能节点的问题;
5,ConstantHashLoadBalance—>>一致性Hash,即确定的hash确定的参数,适用于所有状态请求(简单的说就是同一用户相同的请求会发送给同一提供者)
如果恰巧该节挂了,会平摊到其他提供者上而不会引起剧烈波动;
缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />
缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />
nginx负载均衡策略
1,roundRibon 随机轮询
2,加权轮询
3,ip hash 一致
openfeign负载均衡
- RandomRule-----随机策略
//从所有server数里随机选择一个数
int index = this.chooseRandomInt(serverCount);
//通过index来获得一个server
server = (Server)upList.get(index);
- RoundRobinRule ----简单轮询策略
server = (Server)allServers.get(nextServerIndex);
- RetryRule----重试策略
public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();//记录当前时间
long deadline = requestTime + this.maxRetryMillis;//截止时间
Server answer = null;
answer = this.subRule.choose(key);//先按照RoundRobinRule(轮询)的策略获取服务
if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {//如果没有响应或者当前时间小于截止时间.如果获取的服务失败则在指定的时间会进行重试,进行获取可用的服务。
InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());//将其加入任务
while(!Thread.interrupted()) {//线程没有中断时,继续尝试请求服务
answer = this.subRule.choose(key);
if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {//超时则退出,
break;
}
Thread.yield();//让渡cpu
}
//多次获取某个服务失败,就不会再次获取该服务。
task.cancel();//任务取消
}
return answer != null && answer.isAlive() ? answer : null;
}
WeightedResponseTimeRule ----响应时间加权策略
刚开始统计数据不够使用的是RoundRobinRule---->简单的轮询策略
统计数据够了在切换为WeightedResponseTimeRule
- AvaliabilityFilterRule—可用过滤策略
过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务,然后对剩余的服务列表安装轮询策略进行访问。 选择10次都没有合适的则找上级父类的轮询策略;
public Server choose(Object key) {
int count = 0;
//轮循选一个,判读是否满足条件,如果满足则返回,超过10次,则调用父类的choose方法选择.
for(Server server = this.roundRobinRule.choose(key); count++ <= 10; server = this.roundRobinRule.choose(key)) {
if (this.predicate.apply(new PredicateKey(server))) {//遍历服务列表,过滤掉不满足条件的server
//在满足条件的服务列表中,再进行RoundRibbon算法,选出服务---server = this.roundRobinRule.choose(key)
return server;
}
}
return super.choose(key);
}
- BestAvailableRule---->>最低并发策略
会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。逐个找服务,如果断路器打开,则忽略。
public Server choose(Object key) {
if (this.loadBalancerStats == null) {
return super.choose(key);
} else {
List<Server> serverList = this.getLoadBalancer().getAllServers();
int minimalConcurrentConnections = 2147483647;//并发连接数
long currentTime = System.currentTimeMillis();
Server chosen = null;
Iterator var7 = serverList.iterator();
while(var7.hasNext()) {
Server server = (Server)var7.next();
ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);//获得server状态serverStats
if (!serverStats.isCircuitBreakerTripped(currentTime)) {//判断非熔断状态
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);//获得当前时间的并发数
if (concurrentConnections < minimalConcurrentConnections) {//如果当前并发数小于给定的值,则最小并发数为concurrentConnections
minimalConcurrentConnections = concurrentConnections;
chosen = server;//就选并发数小的server
}
}
}
if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
}
**ZoneAvoidanceRule(区域权衡策略)**默认策略
复合判断Server所在区域的性能和Server的可用性,轮询选择服务器。
zookeeper的轮询策略
Ø 轮询(RoundRobin):将请求顺序循环地发到每个服务器。当其中某个服务器发生故障,AX就把其从顺序循环队列中拿出,不参加下一次的轮询,直到其恢复正常。
Ø 比率(Ratio):给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常;实际就是加权
Ø 优先权(Priority):给所有服务器分组,给每个组定义优先权,将用户的请求分配给优先级最高的服务器组(在同一组内,采用预先设定的轮询或比率算法,分配用户的请求);当最高优先级中所有服务器或者指定数量的服务器出现故障,AX将把请求送给次优先级的服务器组。这种方式,实际为用户提供一种热备份的方式。
Ø 最少连接数(LeastConnection):AX会记录当前每台服务器或者服务端口上的连接数,新的连接将传递给连接数最少的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
Ø 最快响应时间(Fast Reponse time):新的连接传递给那些响应最快的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
以上为通用的负载均衡算法,还有一些算法根据不同的需求也可能会用到,例如:
Ø 哈希算法( hash): 将客户端的源地址,端口进行哈希运算,根据运算的结果转发给一台服务器进行处理,当其中某个服务器发生故障,就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
Ø 基于策略的负载均衡:针对不同的数据流设置导向规则,用户可自行编辑流量分配策略,利用这些策略对通过的数据流实施导向控制。
Ø 基于数据包的内容分发:例如判断HTTP的URL,如果URL中带有.jpg的扩展名,就把数据包转发到指定的服务器。
88.乐观锁于悲观锁
- 乐观锁的概念:
乐观锁在操作数据时认为别人不会同时修改数据,因此也不会上锁,只是在执行更新的时候判断一下在此期间是否有人修改了数据,如果别人修改了数据,则放弃此次操作,否则执行操作;
- 乐观锁实现机制:
CAS 与版本号机制
CAS是由CPU支持的原子操作,其原子性是在硬件层面进行保证的。
使用场景
乐观锁适用于写比较少的情况下(多读场景),这样可以省去了锁的开销,加大了系统的整个吞吐量。
悲观锁的概念:
悲观锁在操作数据时认为别人会同时修改数据,因此操作数据时会先加锁,知道操作完成后才会释放;
- 悲观锁实现机制:
syshonized ,lock等能实现加锁的方式
使用场景:写操作多场景;
89.sleep与wait方法的区别
sleep 是Thread中的静态方法,而wait是Object中的方法;
sleep是使得当前线程睡,如果该线程拿到了锁并不会将锁释放;
wait是使得当前线程阻塞,让出cpu,如果有锁则会释放锁;
90.Java类加载机制
jvm加载类机制分为五步:
1,加载
此时jvm会在内存中生成一个Class对象,反射就是通过这个Class对象来获得类的一些信息的;
2,验证
即验证字节码信息不会危害虚拟机的自身安全;
3,准备
为类中的变量分配内存,设置初始值,
即为变量分配内存之后会先执行变量的初始化完成后再赋值;
比如再加载 static int x =100;是会先 将x 初始为0,然后再赋值为100;
如果是被final修饰的变量,再准备阶段会根据实际值进行赋值,即不初始化就直接赋值;这也符合final修饰变量的特点----不可修改;
4,解析
jvm将常量池中的符号替换为直接引用的过程;
符号引用就是 class 文件中的:
1. CONSTANT_Class_info
2. CONSTANT_Field_info
3. CONSTANT_Method_info 等类型的常量。
5,初始化
初始化阶段是执行构造器的过程
注意以下几种情况不会执行类初始化:
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
- 定义对象数组,不会触发该类的初始化。
- 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
- 通过类名获取 Class 对象,不会触发类的初始化。
- 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
- 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。
91.MVC组件有那些?
前端控制器—DispatcherServlet---->>j接受请求,行营结果(请求转发)
处理映射器—HandlerMapping ---->>根据url来查找处理器
处理适配器----HandlerAdapter---->>处理请求
处理器—Handler---->>处理的逻辑
视图层----ViewResolver–>>负责试图解析
视图---- View —>>试图呈现jsp,thymeleaf
92.Mvc设计模式的好处与常用注解.
好处:
分层设计,实现了各个业务模块之间的 解耦,利于业务系统的扩展与维护
常用注解:
@RequestMapping
用于url上---->>>表示该方法响应对用的请求
用于类上----->>>表示该控制器下的所有相应的请求方法都是以该地址作为父路径的;
@GetMapping.@PostMapping略
@RequestBody
实现接收json数据,将json转换为java对象
@ResponseBody
将方法返回值解析为json对象响应给用户;
@Param 参数设置别名
@RestControlller 该控制器下所有的请求返回值都被解析为json数据然后返回给用户;
@RequestMapping 注解的属性---->>
- 1,value:
指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
- 2,method:
指定请求的method类型, GET、POST、PUT、DELETE等;
- 3,consumes:
指定处理请求的提交内容类型(Content-Type),例如 application/json, text/html;
- 4,produces:
指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
- 5,params:
指定request中必须包含某些参数值是,才让该方法处理。
- 6,headers
: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
一般常用的也就是 value 与method,
下面是一个 HTTP 请求的请求头:
GET /home.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/testpage.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT
If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a"
Cache-Control: max-age=0
严格来说在这个例子中的 Content-Length 不是一个请求头,而是一个实体头(entity header):
POST /myform.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Content-Length: 128
93.@PathVariable和@RequestParam的区别
@PathVariable---->>REST风格的路径变量
@RequestMapping(value = “/page/{id}”, method =
RequestMethod.GET)
@RequestParam用来获得静态的URL请求入参 spring注解时action里用到 .
94Spring MVC与AJAX
通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。
具体步骤如下 :
(1) 加入Jackson.jar
(2) 在配置文件中配置json的映射
(3) 在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。
95.怎样在方法里面得到Request,或者Session?
直接在方法上声明该参数,mvc自动将其传入方法中;
96.MVC拦截器
<!‐‐ 配置Spring MVC的拦截器 ‐‐>
<mvc:interceptors>
<!‐‐ 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 ‐‐>
<bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></b
ean>
<!‐‐ 只针对部分请求拦截 ‐‐>
<mvc:interceptor>
<mvc:mapping path="/modelMap.do" />
<bean class="com.zwp.action.MyHandlerInterceptorAdapter" />
</mvc:interceptor>
</mvc:interceptors>
97.Mybatis的运行原理
通过mybatis配置文件来生成sqlsessionfactory对象由工厂方法获得sqlsession,
sqlsession解析执行sql,
Mybatis都有哪些Executor执行器?它们之间的区别是什么?
- Mybatis有三种基本的Executor执行器
SimpleExecutor、ReuseExecutor(执行器可重用,存在map里面)、 BatchExecutor(批量执行器)。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使 用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添 加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行 executeBatch()批处理。与JDBC批处理相同。
98.Mybatis的一级、二级缓存
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session, 当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓 存。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储, 不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默 认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接 口(可用来保存对象的状态),可在它的映射文件中配置 ;
3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的 进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
99.数据库三大范式是什么
1,第一范式:
字段不可再拆分;
2,第二范式:
非主键完全依赖于主键而是不是主键的一部分;
4,第三范式:
非主键列只依赖于主键,不依赖于其他非主键。
100.Mysql中的锁
innodb中支持表锁与行锁
myisam中支持表锁
dbd中支持页面锁与表锁
表锁加锁快,开销小,不会出现死锁,但是锁定范围大并发度低;
行锁加锁慢,开销大,容易出现死锁,发生锁冲突的概率小,并发度高;
行锁是给索引项加锁来实现的,所以只有通过索引修改数据才会有行锁,如果没有通过索引项来操作数据则用的是表锁;
排他锁
mysql中获得共享锁----->>即读锁
sql语句 lock in share mode ;
显示指定 共享锁
mysql> start transaction ;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from goods where goodsid = 12 lock in share mode ;
+---------+-----------+------------+-----------+
| goodsid | goodsname | goodsprice | goodsdesc |
+---------+-----------+------------+-----------+
| 12 | 肥肠鱼 | 46 | 香辣 |
+---------+-----------+------------+-----------+
1 row in set (0.00 sec)
另一用户修改该数据时,会被阻塞,直至超时
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from goods ;
+---------+-----------+------------+--------------+
| goodsid | goodsname | goodsprice | goodsdesc |
+---------+-----------+------------+--------------+
| 12 | 肥肠鱼 | 46 | 真好吃 |
| 14 | 面包蟹 | 88 | 蟹黄 |
| 13 | 酸菜鱼 | 57 | 又酸又菜 |
+---------+-----------+------------+--------------+
3 rows in set (0.00 sec)
mysql> update goods set goodsid = 14 where goodsid =12 ;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
#^CEnter password: #ctrl+c中断命令;
#^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
mysql> update goods set goodsid =17 where goodsid =13 ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
同时我们可以发现,不会阻止其他用户对改行数据的读操作(共享锁);当操作数据与所以有关时加的是行锁,操作其他行数据是可以的;
mysql中获得排他锁----->>即写锁
当一个线程获取写锁之后,会阻塞其他用户对改行数据的读写操作;
行锁的实现方式—
InnoDB行锁是给索引项加锁来实现的。所以只有通过索引项来操作数据才会有行锁,如果没有操作索引项 用的则是表锁;
默认情况下innodb用的是隐式加锁–
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加写锁–排他锁(X)因为要操作数据;
对于普通SELECT语句,InnoDB不会加任何锁,因为不需要操作数据,只是读取数据;
mysql锁的一些建议
尽量控制事务的大小,减少锁定资源量和锁定时长;
数据检索最好是通过索引,因为时间快性能较好(当然数据量小也会进行全表扫描),同时也可以避免升级成表锁–合理使用索引可以更好的提高性能;
尽可能减少在数据范围内的检索条件比如 检索条件 <50 要比 <100要好(这要求我们大致推算出数据的位置),避免间隙锁带来的负面影响而不能操作一些数据;
在合适的情况下尽量使隔离级别较小,同时最好是一次锁定所有的所需操作的资源;
未完待续