写在前面
当自己真正开始梳理以往学过的知识,会发现很多点有遗忘或者当时了解的就不是很透彻。可以参考相关学过的课程,和别人的简历对自己的技术栈进行一个梳理
后面反思了一下
后面插入一条主线,其实以下问题是基于JUC开发的相关问题。为什么以前自己不够了解这些问题,或者说了解完容易忘记,因为做的很多项目都不是并发的,没用线程,也没用事务、更没用多事务,多人同时登陆。所以姑且这块叫做**JUC面试部分吧!**而且我觉得再小的公司也得有并发的情况啊,所以我觉得这块还是挺重要的,后期想真正的落实到项目中。
1.这一块的理解今天又加深了一下(不知正确与否),其实在实际生产中,还是主要在dao层这块,在数据库这块。http协议以及服务器的架构,他们本身就支持很好的隔离机制,只有到我们自己操作crud到数据库或者缓存中的时候,那时候我们的多线程之间对同一块内存或者同一行数据进行操作的时候,要格外小心。
理解这部分的话,第一要理解资源的概念,比如说i现在这个整形,在并发下就是一个资源,有可能两个线程同时读到了他,但是而且要进行相应的操作。这是资源的理解,像同步代码块,同步方法都是java里面对并发下资源的控制。
第二理解并发的话,重点理解和观察出哪里是非原子性操作,什么叫非原子性操作。就是说白了,我这个操作,我执行的时候,不可能切出去,别的线程不可能打断。就一步就能完事的操作。
你去搜,有基本三大类,我自己是不太熟悉。我的经验就是一旦是两行代码了,尤其if这种基本不是了,两行你就有情况先判断然后等着,又来一个判断,然后你去操作,他操作的时候很明显就把你的操作覆盖掉了(我说的是cpu轮询的时候)。
再一个++这种操作,或者i+=n这种操作(这个操作类型,在hashmap扩容的时候就有,现有长度等于原长度+扩容长度)–所以hashmap不安全
补充的话 还有,除了 long double 基本类型的复制,就直接一个=号这种;引用这是原子性的操作,还有一个原子包里面都是原子性的,所以说运算来说非原子性的概率很大
线程的五种状态
新建就你new了什么的 就绪
start执行了以后就就绪了–sleep与wait()的notify()也是就绪状态等着操作系统来找你
run不用说跑起来了
阻塞状态,阻塞、就绪、运行三者之间一个三角。阻塞之后只能就绪,不能直接运行。那么说sleep和wait都是让线程进入阻塞态
死亡状态
1.说下Sleep和wait的区别
sleep()方法
sleep方法属于线程类。
sleep的线程,首先不一定是调用了同步锁。
如果调用了同步锁的话,它不会释放掉同步锁。
他仅仅让出cpu,但是锁住的相关对象其他线程无法调用。
当sleep时间到的时候,会自动重新抢夺cpu资源,但是不一定能够抢夺到。
wait()方法
wait方法属于object类,同时notify()也是。
这两个哥们配合使用,表示线程挂起与恢复。
此时线程一定调用同步锁,并且他会释放掉同步锁,继续回到线程池中等待。
等待其他方法调用notifiy()来通知他参与到cpu的竞争(此时解除阻塞状态,但是要看是否wait()方法设置了超时时间,如果设置了超时时间,不用等到其他进程使用notifiy方法,一样会解除阻塞),但是不是notifiy()方法一调用他就能拿到cpu资源,此时还是要等待其他方法调用wait(),这样资源才会被释放掉。
相比较sleep方法可以在任意位置执行,wait只能在同步代码中执行。
wait方法使用时要捕获异常。
synchronized与Lock区别
首先synchronized是java的一个关键字,而Lock是一个接口
关键字会有锁升级等过程,这个以后再聊,关键字不会造成死锁,接口会造成死锁,所以说你在finally里面必须给他解开、释放掉
。关键字会释放掉自己手里的资源,分为两种,要是我拿到这个锁,我会执行完,就放。我要有异常,jvm会帮关键字释放锁。
但是Lock可以通知线程相应终止等待资源,但是关键字不会他会老老实实让其他线程等着,等着我获得这个锁。
关键字适用于少量同步的时候,接口适用于大量同步的时候。因为接口可以实现读写分离,一块读,一块写。简单来说接口比较灵活,关键字比较单一,竞争不激烈的时候使用关键字管理就行。
聊聊String、StringBuffer、StringBuilder
stirng
*说说到底是哪
下面这行代码出现在append里面,设想两个线程同时操作这种非原子性的运算,可能线程1算完是11,线程2算完也是11 这个值就等于11了,但其实应该是12.而且扩容还有可能引起复制吧,复制的时候有可能你就给别的线程的值覆盖上。
count += len;
public class lazy {
private static lazy lazy=new lazy();
private lazy(){
System.out.println("iiii");
}
private static lazy getLazy(){
return lazy;
}
实验内容:网上这块说这个私有的对象,因为要在静态的方法里,返回给调用者,所以他是静态的,我觉得太牵强了。结合上面说的这个关键字给我的感觉,私有属性对象这句话,给我的感觉就像静态代码块,所以我做了一个小实验(原理不懂,如果大佬知道请明示)
我首先没写返回这个对象的静态方法,我直接这么写的
我发现,他也是执行构造函数的(预期之内),但是当我把static去掉的时候,构造函数就不执行了。所以说这个字感觉还是很有意思的
public class lazy {
private static lazy lazy=new lazy();
private lazy(){
System.out.println("iiii");
}
集合是面试的重头,个人理解,后端嘛,那还是多跟数据打交道,其实集合就是不同的存储数据的方法。你要看的话,一看性能:查找的性能,增删的性能。二看,安全。两者平衡吧。
还得是机械键盘,速度这一块,来吧展示
首先大的方面有Collection与Map这两大接口,但是他们剥离的不是很彻底。set与map之间有很大的关系
转载一张图。
转载说明
https://www.cnblogs.com/bingyimeiling/p/10255037.html
常问的说下list set 与map
mysql的结构,两大部分
1.服务端,这里面控制大部分的服务.
2.插件式存储引擎(在Mysql 5.5 以后 innodb变成默认,常用的三种,innodb、Mysiam、memory)
一条查询语句要执行的流程
一条更新语句要执行的流程
说说mysql如何支持回滚(只有innodb支持事务)
两段式提交以及redo log 和 binlog
redo log顺序存储、binlog追加存储
先写到redo log里面,在一起提交
事务四大特性 常说的ACID–来自菜鸟教程
之前的理解很多被网上带偏了 请看下面菜鸟教程官方
1.原子性(atomic吧,我瞎说的啊),事务不可再拆分(现在暂时没有好的理解,就比如说转账这个业务,就是不能再拆分了,拆分以后,一方加钱,一方不动,那就不符合转账这个业务了,就是说对于这个业务不可再分。。?)
2.一致性:事务里面所有DML要么全部成功,要么全部失败。这事要么做成,要不不做,没有中间态。
3.隔离性:这个性质里面又有文章,简单说就是什么时候可见的性质。
4.持久性(想到了dao层。。取前一个字母D)
其实四大性质中,都是为了保证一个的一致性
自我理解
原子性就是:不成功便成仁!
一致性就是:假设说转账业务,原来双方一共有1000,你转完必须还是有1000,不能说凭空多了或者少了
隔离性就是:多事务并发执行中,你怎么定义你对某些数据的隔离性,说白了很像java里面的共享数据、或者加锁
持久性就是:就是一锤定音呗,写入了就是写入了。上午棱天地合,也不可能变了。
官方定义
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
再说我们四大特性中最重要的隔离性前,看看并发访问数据库会出现什么情况
但是!隔离的越严实,效率越低!从上到下,效率越来越低!
前提,你有两个并行执行的事务,他们之间一个是正常的DML操作,两一个就是执行select操作,下文我们定义查询操作叫我们的操作
脏读:你读到了人家还没提交的事务。这边人家确实dmlL 但是还未提交所以不符合逻辑。
幻读:幻读首先针对仅仅针对于Insert这种DML操作,我们本身的事务是统计行数的查询语句,前后行数不一致。 这有点跟下面的不可重复读差不多,只是幻读针对的是insert语句的查询,查询行数。
不可重复读:你读取前后数据不一致,假如说那边更新了下数据,我们的事务之前读的是1,后面人家改了以后,就是2了,最后你看下,唉?2!
后面查了一下官方定义也想了一下,不仅仅是insert啊,假如说delete删掉这一行呢,也会出现幻读的状况。幻读官方的定义就记住,前后查询的行数不一样。
另外,脏读是读取到人家没提交的,幻读和不可重复读是,你提交不提交我不管,我数据不一致。
事务隔离级别—四种
RU–RC–RR–SR
你别看是四种隔离级别对于我们JUC来讲,掐头去尾就剩两种。
第一种不要是因为,上面说的脏、幻、不可,他一个都没解决。。。
第四个不要是因为他压根直接把线程变成串行了。但是这块要明确一下,这是读写串行化,并不是完全意义上的串行化,只有读写这一步是串行的(当然效率依然很低)—其实我自己理解读写串行化对于数据库而言就是完全串行化了啊,数据库不就是读写吗?还干啥啊??但是他可能相对于整个完整的线程,这一部分是读写串行化的。
读未提交:三种情况全部没解决。
读提交(oracle数据库默认隔离级别)
能避免脏读,但是对于不可重复度、幻读还是可能会出现。
可重复读(mysql数据库隔离级别)
幻读可能会出现;不可重复读、脏读解决。
串行化
串行化是隔离级别最高的,咱们之前说这些知识都是包括在JUC(并发编程)里面的。那你直接串行了,读写线程(读写串行!或者说线程中的读写部分)一个接一个走,老老实实的排队,当然不会出现幻读啊、脏读啊、不可重复读啊。
下面有一个经典案例,两个事物纠缠在一块,看下v1、v2、v3的值
RU因为是什么也没隔离:v1=20 v2=20 v3=20
RC避免了脏读:v1=18 v2=20 v3=20
RR在rc的基础上解决了不可重复读:v1=18 v2=18 v3=20
ser串行不存在并发执行,那就得指定顺序了
–此图来自享学课堂
这个再往后是要学习LBCC以及MVCC就是如何实现底层隔离的!
到底什么是索引
先说下常见的数据结构
1.hash表–躲不过去的
下面这个博主说的非常全面了
https://www.cnblogs.com/zhuyeshen/p/12082839.html
索引模型–查找顺序–什么是回表
聚簇索引与非聚簇索引
就是一个是以主键为索引(聚簇索引),另一个不是(非聚簇索引)。(假设不创建主键也会有一个隐藏的主键)。
两者的叶子节点也存着不同的数据,聚簇索引比较厉害,他的叶子存的是这行数据。而非聚簇叶子节点存的是主键的值。
查询顺序是咋样的、什么叫回表
查询顺序得看你是用那种索引了,聚簇还是非聚簇的。
看到上面两种索引的机构我觉得你能感觉到,这两种查询区别很大,简单来说就是非聚簇查到以后,还要根据他叶子节点上主键的值,再回头查下’以聚簇为索引的这个b+树。上面这个过程叫回表。
为什么自增主键是一个优化的选择
怎么减少回表