网站系统网站开发,网页版传奇霸主攻略,网站建设佰首选金手指五,wordpress主要标签可以说#xff0c;学姐给我的这份文档真的把我的知识查漏补缺#xff0c;面试问到了好多#xff0c;值得收藏。 并发编程
一.Executor
为什么使用线程池#xff1a;手动创建线程耗费性能#xff0c;不利于管理。
首先创建线程池有两种方式#xff1a;使用Executors工厂… 可以说学姐给我的这份文档真的把我的知识查漏补缺面试问到了好多值得收藏。 并发编程
一.Executor
为什么使用线程池手动创建线程耗费性能不利于管理。
首先创建线程池有两种方式使用Executors工厂来创建ThreadPoolExecutor这类自定义线程池。
1. 使用Executors工厂来创建
Executors是一个类用来创建线程池常用的有四种线程池
1.newFixedThreadPool 创建一个可重复固定的线程数的线程池
2.newCachedThreadPool 创建一个可缓存的线程池调用execute将重复用以前构造的线程如果当前线程可用。如果没有可用线程则创建新的线程并加入到池中。终止并从缓存中移除那些已有60s未被使用的线程。
3.newSingleThreadExecutor创建一个单线程化的线程池它只会用唯一的工作线程来执行任务保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
4. newScheduledThreadPool 创建一个支持定时及周期性的任务执行的线程池多数情况下可用来替代Timer类。
使用示例 2. 使用ThreadPoolExecutor这个类自定义创建线程池 二.ThreadLocal 三.中断线程
1.线程执行完毕会自动结束
2.在线程处于阻塞期限等待火无期限等待时调用线程的interrupt会抛出interruptedException异常从而提前终止线程
3.若没有处于阻塞状态调用interrupte将线程标记为中断此时在调用interrupted判断线程是否处于中断状态来提前终止线程。
4.线程池调用shutdown等待所有线程执行完毕之后关闭 三.Volatile关键字的理解
两层语义1.保证不同线程对该变量的内存可见线 2.禁止进行指令重排序 内存语义
读这个变量的时候JMM会把本地内存中的变量设置为无效从主内存中取
写这个变量的时候JMM会把本地内存变量刷新到主内存当中
实现添加Volatile关键字会在汇编代码中多处一个kock前指令也就是内存屏障 四.Synchronized关键字
1synchronized是Java中的关键字是一种同步锁。它修饰的对象有以下几种 1. 修饰一个代码块被修饰的代码块称为同步语句块其作用的范围是大括号{}括起来的代码作用的对象是调用这个代码块的对象 2. 修饰一个方法被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象 3. 修饰一个静态的方法其作用的范围是整个静态方法作用的对象是这个类的所有对象
4. 修饰一个类其作用的范围是synchronized后面括号括起来的部分作用主的对象是这个类的所有对象。 2Synchronized的作用主要有三个1确保线程互斥的访问同步代码2保证共享变量的修改能够及时可见3有效解决重排序问题。
3Synchronized 原理 在编译的字节码中加入了两条指令来进行代码的同步。 monitorenter 每个对象有一个监视器锁monitor。当monitor被占用时就会处于锁定状态线程执行monitorenter指令时尝试获取monitor的所有权过程如下 1.如果monitor的进入数为0则该线程进入monitor然后将进入数设置为1该线程即为monitor的所有者。 2.如果线程已经占有该monitor只是重新进入则进入monitor的进入数加1. 3.如果其他线程已经占用了monitor则该线程进入阻塞状态直到monitor的进入数为0再重新尝试获取monitor的所有权。 monitorexit 执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时monitor的进入数减1如果减1后进入数为0那线程退出monitor不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 通过这两段描述我们应该能很清楚的看出Synchronized的实现原理Synchronized的语义底层是通过一个monitor的对象来完成其实wait/notify等方法也依赖于monitor对象这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
4Synchronized的优化
4synchronized与Lock的区别 lock是一个类主要有以下几个方法 lock()获取锁如果锁被暂用则一直等待 unlock():释放锁 tryLock(): 注意返回类型是boolean如果获取锁的时候锁被占用就返回false否则返回true
tryLock(long time, TimeUnit unit)比起tryLock()就是给了一个时间期限保证等待参数时间 1Lock的加锁和解锁都是由java代码实现的而synchronize的加锁和解锁的过程是由JVM管理的。 2synchronized能锁住类、方法和代码块而Lock是块范围内的。 3Lock能提高多个线程读操作的效率
4LockLock实现和synchronized不一样后者是一种悲观锁它胆子很小它很怕有人和它抢吃的所以它每次吃东西前都把自己关起来。而Lock底层其实是CAS乐观锁的体现它无所谓别人抢了它吃的它重新去拿吃的就好啦所以它很乐观。如果面试问起你就说底层主要靠volatile和CAS操作实现的。
五.CAS机制
Compare And Swap
CAS操作是一个原子操作由一条CPU指令完成使用了3个基本操作数内存地址V旧的预期值A 要修改的新值B
需要更新一个变量的时候只有当内存地址V中的值和预期值A相等的时候才会修改为新值B ,如果失败则自旋重新尝试
问题
1.ABA 解决办法加一个版本号只有当版本号和预期值都符合的时候才修改
2.不能保证代码块的原子性CAS机制知识保证一个变量的原子操作
五.JMM的理解 JMM的核心是找到一个平衡点在保证内存可见性的前提下尽量的放松对编译器和处理器重排序的限制。 可能会产生缓存一致性的问题
1.总线锁定
2.缓存一致性协议 三大特性原子性可见性有序性 六.JVM的锁优化
1.自旋锁和自适应自旋锁
线程挂起和恢复都需要很大的性能开销很多共享数据的锁定状态只持续很短的时间为这段时间挂起恢复不值得所以可以让现场进入一个自旋状态但仍占用cpu的时间默认是10次超过10次则采用传统的方式挂起线程。自适应自旋锁会根据上次在这个锁上自旋的时间调整自选次数 2.锁消除
检测到对局部变量加锁会自动将锁消除如在一个方法里面有sb s1 s2 s3以前会转化为StringBuffer线程安全有加锁的append操作优化之后会转换成StringBuilder的sppend操作 3.锁粗化
对一个对象反复加锁解锁如上述的append方法编译器会优化只在最外层加一个锁 4.轻量级锁
对象有一个对象头对象头分两部分区域一部分存储子树的hashcodeGC分代年龄锁标志位等官方成为mark word另一部分用于存储指向方法区对象类型的指针
轻量级锁的执行过程是代码进入同步块的时候如果当前对象没有被锁定锁标志为是01则先在线程的栈帧中建立一个锁记录空间lock record将对象的mark word拷贝过来然后利用cas操作尝试将对象的markword更新为指向lock record如果成功则当前线程拥有该对象锁并且将锁标志位从01改为00如果更新失败则会检查当前对象markword是否指向当前线程的栈帧如果是则直接进入同步块执行否则说明当前的对象锁已经被其他线程占有。如果有两条以上的线程竞争这个锁那就会膨胀成重量级锁锁标志为变成10 5.偏向锁
锁对象第一次被线程获取的时候会将当前的锁标志设置为偏向锁01状态并使用cas操作将当前线程的id记录在markword当中如果成功那么在以后的操作当中不需进行任何操作就可以进入同步代码块当有另外一个线程获取当前锁的时候偏向模式就宣告结束撤销偏向锁之后恢复到未锁定或者轻量级锁状态 JAVA基础
String
1.string s1 “aaa” 与 String s1 new String(aaa)
前者会在Stringpool中创建一个对象如果Stringpool中已经有了则直接引用
后者会在Stringpool和堆中分别创建一个对象如果StringPool中已经有了则只创建一个对象 2.调用s1.integer方法可以将字符串放入StringPool中如果已经存在则直接返回这个对象 继承
1.几种修饰符的访问权限范围 2. equals hashcode的关系
1.是比较两个对象的地址是否相等
2.equals默认和 一样也是比较地址也可以根据自己的需求来重写什么叫做相等
3.hashcode 是根据对象的地址来返回一串int型数字如果对象不一样的话返回的值也不一样 3.为什么重写equals的同时也要重写hashcode
首先必须满足如果equals相同那么hashcode也必须相同如果hashcode不相同那么equals必须不相同的原则否则会导致该类无法与基于散列值的集合类hashmap hashset hashtable结合正常运行 因为对于这些集合在进行重复性判断时是先用hashcode判断如果相同在用equals判断 4.hash冲突的解决方法
1.开放地址法线性探测法二次探测法
2.在散列函数法
3.链地址法 5.hashtable与hashmap的区别
1.hashmap线程不安全hashtable线程安全
2.hashmap最多允许一个键为null而hashtable不允许
3.hashtable中默认的hash数组大小为11增加的方式是old*21而hashmap默认是16肯定是2的指数
4.计算hash的方法不一样hashtable是直接用hashcode而hashmap使用了key的高16位和低16位做异或运算 Linkedhashmap继承于hashmap可以按顺序读取底层使用的是双向链表
TreeMap实现了sortmap接口可以对元素进行排序底层使用的红黑树 7.java BIO NIO AIO 序列化
字节操作使用inputStream和outPutSream实现字节是传输和存储的最小单位
字符操作inputStreamReader将字节流解码成字符流
OutPutStramWriter将字符流编码成字节流
对象操作序列化就是将对象转换成字节序列方便存储和运输两个用途
1.把对象的字节序列永久地保存到硬盘中通常存放在一个文件里
2.在网络上传送对象的字节序列
在很多应用中需要对某些对象进行序列化让他们离开内存空间入住到物理磁盘方便长期保存比如session对象当有大量用户并发访问的时候可能会出现10万个session对象内存吃不消此时就需要将这些session先序列化到硬盘中等要用的时候在把对象还原到内存中。
当两个进程在进行远程通信的时候彼此可以发送各种类型的数据无论是何种类型的数据都会以二进制的序列在网络上传送。发送方需要把这个java对象转换成字节序列才能在网络上传送接收方则需要把字节序列恢复成java对象。
Io与NIO的区别
1.NIO是非阻塞的
2.NIO是面向缓冲区的IO是面向流的 AIO异步非阻塞 8.static关键字的作用
一.修饰变量
1.静态变量在类加载的时候被创建并初始化只被创建一次类加载只进行一次可以修改
2.静态变量属于整个类而不属于某个对象。 二.修饰方法
1.可以通过类名来访问不需要创建对象来访问可以用来实现单例模式
2.静态方法只能调用静态方法和静态变量 三.修饰静态代码块
在类加载的时候执行且只执行一次 9.单例模式
实现 1.为什么要用判断双重 因为可能有两个线程都执行完了第一个if语句如果没有第二重判断那么当其中有个线程执行完synchronized里面的语句之后另外一个线程跟着也会执行这样就达不到单例模式的效果 2.第一重判断去掉也可以实现为什么不去掉
这个设计性能问题因为
参考https://qinjiangbo.com/mechanism-of-double-locking-check.html 10.this与super关键字 9.java中的多态
分为两种
1.编译时多态体现在重载方法名相同而参数不同在编译时期就根据传入的参数确定好调用哪个方法
2.运行时多态体现在方法的重写。在运行时期判断引用类型的实际类型根据实际的类型调用其相应的方法
当父类对象引用变量引用子类对象时被引用对象的类型决定了调用谁的成员方法引用变量类型决定可调用的方法。如果子类中没有覆盖该方法那么会去父类中寻找。 参考链接https://www.runoob.com/w3cnote/java-polymorphism.html 10.接口类和抽象类的异同
区别
1.抽象类可以有非抽象方法但接口只能有抽象方法
2.接口中的成员变量默认修饰为public static final高度抽象的模版所以这些都是提取出来的不变特征方法默认修饰为public abstract。抽象类中的成员变量可以被不同的修饰符来修饰
3.类可以实现多个接口但只能继承一个抽象类 相同
1.不能被实例化
2.派生类都必须实现未实现的方法 11.instanceof
用来判断一个对象是否是一个类的实例 12.各种排序算法复杂度及稳定性 1.java底层如何实现排序的
Java中Arrays.sort使用了两种排序方法快速排序和优化归并排序。
快速排序主要针对基本的数据类型int short long排序而归并排序用于对象类型的排序 如果数据量小于60会使用插入排序插入排序是稳定的 13.java中的堆和栈
栈主要用于存储局部变量和对象的引用变量每个线程都有一个独立的栈空间所以线程之间是不共享数据的
栈的特点是存取速度快但所存数据的大小和生存期必须是确定的编译后就已经确定大小 堆堆中主要存储实例化的对象和数组。线程共享。堆的优势是可以动态的分配内存大小生存期也不必事先告诉编译器但缺点是存取速度较慢 Linux基本面试题
Ls用于显示指定工作目录下的类容不会列出详细信息
Ll会列出当前文件目录的详细信息含有时间权限大小等
Cd:用于切换到目标目录
Mkdir用于简历当前目录的子目录
rm-r 删除的文件或者目录需确认
rm –rf同上但无需确认
cp复制文件 若复制目录则必须加上-r
数据库MySQL
一.索引
1.B树B树以及两者的区别
B树是一种多路平衡查找树其每一个节点都存储Key和data
B树是B树的一个变种叶子节点存储data非叶子节点只存储keyB树的叶子节点增加了顺序访问指针每一个叶子节点都可以访问到他的下一个叶子节点 区别
1.B树种只有叶子节点会带有全部信息非叶子节点只起到索引的作用二B树的所有节点都带有全部信息B树的每一层节点都会再次出现在下一层节点上
2.B树种所有叶子节点都是通过指针连在一起B树则没有 2.索引的优点和缺点
优点可以加大检索速度
缺点创建和维护索引需要耗费时间 3.Mysql为什么选择B树
Mysql数据本质上是放在外部存储的B树是为了加快读取速度二设计的一种数据结构
1.可以减少i/o次数只有叶子节点才存储数据非叶子节点存储索引这样一次读取到内存的关键字增多相对i/o次数也就减少根据区别一
2.能够提供稳定高效的范围扫描因为所有的叶子节点都互相连接根据区别二 4.索引越多越好吗
索引可以提高select的效率但是也降低了insert和updata的效率因为插入和更新的时候可能会重建索引索引怎么建索引要慎重考虑。 5.索引分类
1.B树索引以b树作为数据结构的索引
2.hash索引能以O(1)的时间复杂度查找但失去了有序性innodb有一个自适应哈希索引当这个索引值被频繁使用时会在b树上创建一个哈希索引
3.全文索引用于查找文本的关键词中文需要由中文分词插件
二. MySQL优化
一.MySQL的优化主要分为索引的的优化sql语句的优化表的优化。同时可以使用缓存增加效率
1.索引的优化
只要列中含有null最好不要再此列设置索引
对于经常在where语句中使用的列最好设置一个索引
对于like语句以%或者-开头的不会使用索引以%结尾会使用索引 二.sql语句的优化
查询优化要尽量避免全表扫描
查询时能不用*就不用*尽量写字段名
三. MySQL常问问题
1.数据库如何应对大规模的写入和读取
1使用NoSQL通过降低数据的安全性减少对事物的支持减少复杂查询的支持来获取性能的提升但有些场合NoSQL无法满足要求
2分库分表
水平切分不修改数据库的表结构通过对表中的数据拆分而达到分片的目的一般水平切分在查询的时候可能会用到union操作多个结果并
可以根据hash或者日期来进行分表 垂直切分修改表结构按照访问的差异将某些列拆分出去一般查询数据的时候可能会用到join操作把常用的字段放在一个表中不常用的放在一个表中把字段比较大的比如text字段拆出来放在一个表中。 分库分表能够解决数据量过大带来的查询效率下降问题但是却无法给数据库的并发处理能力带来质的提升分库可以对关键字取模的方式来对数据访问进行路由 (3)读写分离
读写分离是在主服务器上修改数据数据也会同步到从服务器上从服务器只能提供读取不能写入实现备份的同时也实现了数据库的性能优化 如何保证数据一致性
1主节点
保证事务每次提交之后要确保binlog都能刷新到磁盘中只要有了binloginnoDB就有方法恢复数据不至于导致主从复制的数据丢失
2从节点 开启 relay log 自动修复机制发生 crash 时会自动判断哪些 relay log 需要重新从master 上抓取回来再次应用以此避免部分数据丢失的可能性。 2.数据库事务及其隔离级别
事务的特性ACID 事务在并发的时候隔离性很难保证主要可能出现下面这些问题
脏读一个事务读了另外一个事务未提交的数据如果另一个事务回滚则会发生脏读
不可重复读一个事务前后读取同一行数据如果在这个过程中有其他事务修改了此数据则会发生不可重复读
幻读一个事务前后读取范围的时候 事务隔离级别 MySQL实现事务是基于undo/redo日志实现的 undo日志记录修改前的状态ROLLBACK基于UNDO日志实现
REDO日志记录修改后的状态事务的持久性基于REDO日志实现 两种解决脏读、不可重复读、幻读的方案
MVCC性能较高但读的可能是历史版本
1.版本链对于每一行的数据在undo日志中总会记录每个版本记录以及对应的事务id
2.readView
核心问题当前版本链中哪个版本对当前事务可见
Readview包含的内容{当前活跃的事务id下一个应该分配的事务id当前自己的事务id}根据当前读的版本事务id和这个readview对比如果是活跃的或者大于下一个应该分配的事务id则说明当前版本对此事务不可见应该前读一个版本依次类推直到找到可见的版本
提交读每次读取数据前都会生成一个readview
可重复读在第一次读取时句时生成一个readview 锁性能不高但读的是最新版本 MyISAM和innoDB的区别
1.innodb支持行锁myisam不支持行锁
2.innodb支持事务myisam不支持事务
3.innodb支持回滚和安全回复myisam不支持
4.innodb的索引就是数据myisam的索引只存储了主键和行号还需要根据行号去查找相应的记录
5.innodb更适合写密集的表myisam更适合读密集的表
计算机网络
1.tcp和udp的区别
Udp无连接尽最大可能交付没有拥塞控制流量控制
Tcp面向连接可靠交付有拥塞控制和流量控制 2.输入一条url整个过程
1.DNS解析获取ip地址本机本地域名服务器根域名服务器顶级域名服务器权限域名服务器
2.建立TCP连接
3.浏览器发出http请求
4.服务器进行响应
5.TCP连接释放
6.浏览器渲染 3.为什么是三次握手四次挥手
三次握手防止之前滞留的连接请求再次到达服务端
四次挥手因为tcp是全双工模式客户端停止发送请求之后服务端也要停止发送请求 4.time_wait存在的原因时间是多少两倍的报文最大存活时间
1.确保客户端发送的最后一个报文能被收到服务端可以正常关闭。
2.让所有报文都在网络中消失时间是两倍的最大报文存活时间。 5.tcp的可靠传输靠什么
超时重传如果已经发送的报文在超过时间内没有被确认那么就重新发送这个报文 6.Tcp的滑动窗口
发送方和接收方都有一个滑动窗口 7.TCP流量控制
流量控制是为了控制发送方的发送速率保证接收方来得及接收
通过滑动窗口来控制根据报文知道对方窗口的大小然后根据窗口大小来控制发送速率 8.TCP拥塞控制
如报文过多会导致超时重传又会导致网络更加阻塞 1.慢开始和拥塞避免
慢开始设置初始的报文数量为1
拥塞避免设置一个阈值当报文数超过这个阈值的时候每次报文每次加一如果出现超时领阈值等于当前报文的一半重新执行慢开始 快重传如果收到3个确认报文则重传丢失的那个报文
快恢复这种情况令阈值等于当前报文的一半并令当前发送的报文数等于阈值因为没有出现网络阻塞 http各个版本的区别
http0.9 仅支持GET请求仅能访问html资源
http 1.0 增加了post和head请求
http1.1 增加了长连接一个tcp连接可以发送多个http请求新增了putpatchdelete请求
http2.0 增加了双工模式不仅客户端能发送多个请求服务端也能处理多个请求
Redis
1.redis的数据淘汰策略
当redis内存数据大小达到一定的大小时就会施行数据淘汰策略主要有六种策略 2.数据库和缓存的数据一致性
2.1 mySQL里有2000w数据redis中只存20w的数据如何保证redis中的数据都是热点数据
根据数据淘汰策略先算一下这20W的数据大概占多少内存然后设置redis的内存启用从所有数据集中挑选最近最少使用的淘汰策略 2.2 redis缓存和mysql数据库同步 3.Redis持久化
1.RDB持久化redis默认方式
将某个时间点的所有数据都存在硬盘中如果发生故障将丢失最后一次创建快照的数据
触发RDB快照的条件在指定的时间间隔内执行指定次数的写操作
2.AOF持久化
所执行的每一条指令都会记录到appendonly.aof文件中redis会按照策略将指令存入硬盘中。当redis重启的时候会根据日志文件的内容将写指令从前到后执行一次完成数据恢复的功能 Java异常体系 Error:主要是虚拟机产生与代码编写者无关例如outOfMemoryError Exception:主要有运行时异常和io异常
运行时异常数组越界空指针异常
Io异常找不到文件 发生oom的可能区域
除了程序计数器都有可能发生
虚拟机栈当jvm尝试去扩展栈空间失败时可能会抛出oom异常
堆堆中对象堆积起来无法释放
方法区 四种引用类型
主要体现在对象不可达性和对垃圾回收的影响 强引用只要有强引用指向一个对象就表面对象还活着垃圾回收期不会碰这种对象
软引用只有当jvm认为内存不足的时候才会去试图回收这些对象
弱引用不能使对象豁免垃圾回收只是提供一种对象的访问途径
虚引用不能提供他访问对象仅仅提供一种确保对象呗finalize之后做某些事情的机制 JAVA虚拟机是如果加载类的
1.加载主要是用类加载器启动类加载器扩展类加载器应用类加载器来查找对应类的字节流
双亲委派的好处由于类随着类加载器一起有一种优先级的层级关系从而使基础类得到统一 2.链接
验证确保字节流的安全性不会对虚拟机造成危害
准备为类的静态字段分配内存
解析将符号引用解析成实际引用 3.初始化调用clinit方法为静态变量赋与实际的值执行静态代码块
运行时数据区域 运行时内存区域
1.程序计数器记录正在执行虚拟机字节码的指令地址
2.java虚拟机栈主要存储局部变量表
3.本地方法栈
4.堆存储对象的地方有新生代和老年代
5.方法区存储类信息常量静态变量等信息 内存分配策略
1.对象优先在eden区域分配
2.大对象直接进入老年代
3.长期存活的对象进入老年代会有一个年龄计数器达到指定的阈值就会进入老年代 FullGC触发条件
1.调用system.gc
2.老年代空间不足
3.minor GC时老年代空间分配担保失败