面试题111

勇敢牛牛,不怕困难

JVM

说一下 JVM 的主要组成部分?及其作用?

类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface) JNI    native 

组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能

说一下 JVM 运行时数据区?详细介绍下每个区域的作用?

程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令;

Java 虚拟机栈(Java Virtual Machine Stacks):
执行方法前创建栈帧
   用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
执行方法时:
   栈帧入栈 
执行完毕方法:
   栈帧出栈 

本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;

Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;

    GC垃圾回收: 

-Xms    -Xmx 

方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

1.7
 -XX:PermSize    -XX:PermMaxSize 
1.8
 -XX:MetaspaceSize    -XX:MetaspaceMaxSize 


怎么判断对象是否可以被回收?

一般有两种方法来判断:

引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;

可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

哪些变量可以作为GC Roots

虚拟机栈中 (栈帧中的本地变量表) 引用的对象 
(正在执行的方法中的变量所引用的对象 可以作为GCroot对象)

方法区中类静态属性引用的对象   static 
方法区中常量的引用对象        final 

本地方法栈中引用的对象

Java 中都有哪些引用类型?

强引用:发生 gc 的时候不会被回收。
Object user = new Object();
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
new SoftRefrence(user)
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。

说一下 JVM 有哪些垃圾回收算法?

标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。

分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理或标记清楚算法。

说一下 JVM 有哪些垃圾回收器?

Serial:最早的单线程串行垃圾回收器。
Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
ParNew:是 Serial 的多线程版本。
Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。


新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

- 新生代回收器:Serial、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old、Parallel Old、CMS
- 整堆回收器: G1

新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

简述分代垃圾回收器是怎么工作的?

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。

新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:

把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden 和 From Survivor 分区;
From Survivor 和 To Survivor 分区交换,
From Survivor 变 To Survivor,To Survivor 变 From Survivor。


每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。
分配担保,  存活对象如果幸存者区装不下时  直接进入老年代
大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

Minor GC与Full GC分别在什么时候发生?

新生代内存不够用时候发生Minor GC也叫 Young GC,
老年代内存不够用时候发生Major GC也叫 Full GC 

java中都有哪些类加载器

启动类加载器(Bootstrap ClassLoader)
是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;

扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;

应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

自定义类加载器:通过继承ClassLoader抽象类,实现loadClass方法

哪些情况会触发类加载机制

1.在遇到 new、putstatic、getstatic、invokestatic 字节码指令时,如果类尚未初始化,则需要先触发初始化。    
2.对类进行反射调用时,如果类还没有初始化,则需要先触发初始化。
3.初始化一个类时,如果其父类还没有初始化,则需要先初始化父类。
4.虚拟机启动时,用于需要指定一个包含 main() 方法的主类,虚拟机会先初始化这个主类。

什么是双亲委派模型?

如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

说一下类装载的执行过程?

类装载分为以下 5 个步骤:

加载:根据查找路径找到相应的 class 文件然后导入;
检查:检查加载的 class 文件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
初始化:对静态变量和静态代码块执行初始化工作。

有没有在生产环境下进行过JVM调优,说说过程?

基于 jps + jmap + jprofiler 内存溢出问题排查, 使用 -Xmx -Xms设置调整堆大小


基于 Jps + jstack + jconsole cpu飙高问题排查, 通过对栈快照定位代码问题
TOP  
TOP -p java进程ID H
jstack 通过上面查询的线程ID定位到出现问题的线程


基于 JVM 参数调优

常用的 JVM 性能监控的工具?

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
jps  jinfo jstat jmap jstack 

jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等

idea + jprofiler内存分析

常用的 JVM 调优的参数都有哪些?

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-Xmn500M 设置年轻代为500m;
-XX:PermSize=500M ;  1.8之后采用 MetaspaceSize
-XX:MaxPermSize=500M ;  1.8之后采用 MaxMetaspaceSize
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。
-XX:MaxDirectMemorySize : 设置直接内存的大小

Mysql

1、什么是索引

数据库索引的本质是数据结构,这种数据结构能够帮助我们快速的获取数据库中的数据。

2、索引的作用

当表中的数据量越来越大时,索引对于性能的影响愈发重要。索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。有了索引相当于我们给数据库的数据加了目录一样,可以快速的找到数据,如果不适用索引则需要一点一点去查找数据
简单来说
提高数据查询的效率。

3、索引的分类

- 1.普通索引index :加速查找
- 2.唯一索引
- 3.联合索引(组合索引)
- 4.全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。

4、索引原理

索引的实现本质上是为了让数据库能够快速查找数据,而单独维护的数据结构,mysql实现索引主要使用的两种数据结构:hash和B+树: 我们比较常用的 MyIsam 和 innoDB引擎都是基于B+树的。

hash:(hash索引在mysql比较少用)他以把数据的索引以hash形式组织起来,因此当查找某一条记录的时候,速度非常快.当时因为是hash结构,每个键只对应一个值,而且是散列的方式分布.所以他并不支持范围查找和排序等功能.

B+树:b+tree是(mysql使用最频繁的一个索引数据结构)数据结构以平衡树的形式来组织,因为是树型结构,所以更适合用来处理排序,范围查找等功能.相对hash索引,B+树在查找单条记录的速度虽然比不上hash索引,但是因为更适合排序等操作,所以他更受用户的欢迎.毕竟不可能只对数据库进行单条记录的操作.

5、索引的优点

1.可以通过建立唯一索引或者主键索引,保证数据库表中每一行数据的唯一性.
2.建立索引可以大大提高检索的数据,以及减少表的检索行数
3.在表连接的连接条件 可以加速表与表直接的相连
4.在分组和排序字句进行数据检索,可以减少查询时间中 分组 和 排序时所消耗的时间(数据库的记录会重新排序)
5.建立索引,在查询中使用索引 可以提高性能

6、索引的缺点

1.在创建索引和维护索引 会耗费时间,随着数据量的增加而增加
2.索引文件会占用物理空间,除了数据表需要占用物理空间之外,每一个索引还会占用一定的物理空间
3.当对表的数据进行INSERT,UPDATE,DELETE 的时候,索引也要动态的维护,这样就会降低数据的维护速度,(建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快)。

7、索引操作

对索引的简单增删改查语句要记得
查看表中索引
show index from tableName;
创建索引
CREATE INDEX 索引名 ON 表名 列名;
删除索引
DORP INDEX IndexName ON TableName
分析索引使用情况
explain select 语句

8、分析索引使用情况

explain显示了MySQL如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。简单讲,它的作用就是分析查询性能。explain关键字的使用方法很简单,就是把它放在select查询语句的前面。mysql查看是否使用索引,简单的看type类型就可以。如果它是all,那说明这条查询语句遍历了所有的行,并没有使用到索引。 (最简单的说法,希望能说详细些)

9、哪些字段适合加索引

1.在经常需要搜索的列上,可以加快索引的速度
2.主键列上可以确保列的唯一性
3.在表与表的而连接条件上加上索引,可以加快连接查询的速度
4.在经常需要排序(order by),分组(group by)和的distinct 列上加索引 可以加快排序查询的时间,

10、哪些字段不适合加索引

1.查询中很少使用到的列 不应该创建索引,如果建立了索引然而还会降低mysql的性能和增大了空间需求.
2.很少数据的列也不应该建立索引,比如 一个性别字段 0或者1,在查询中,结果集的数据占了表中数据行的比例比较大,mysql需要扫描的行数很多,增加索引,并不能提高效率
3.定义为text和image和bit数据类型的列不应该增加索引,
4.当表的修改(UPDATE,INSERT,DELETE)操作远远大于检索(SELECT)操作时不应该创建索引,这两个操作是互斥的关系。

11、哪些情况会造成索引失效

1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
2.索引字段的值不能有null值,有null值会使该列索引失效
3.对于多列索引,不是使用的第一部分,则不会使用索引(最左原则)
4.like查询以%开头
5.如果列类型是字符串,那一定要在条件中将数据使用单引号引用起来,否则不使用索引
6.在索引的列上使用表达式或者函数会使索引失效

12、联合索引最左原则

在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到

如创建组合索引 a,b,c     那么查询条件中单纯的使用  b 和 c是使用不到索引的

13、聚簇索引和非聚簇索引

MyISAM——非聚簇索引
MyISAM存储引擎采用的是非聚簇索引,非聚簇索引的主索引和辅助索引几乎是一样的,只是主索引不允许重复,不允许空值,他们的叶子结点的key都存储指向键值对应的数据的物理地址。
非聚簇索引的数据表和索引表是分开存储的。


InnoDB——聚簇索引
聚簇索引的主索引的叶子结点存储的是键值对应的数据本身,辅助索引的叶子结点存储的是键值对应的数据的主键键值。因此主键的值长度越小越好,类型越简单越好。
聚簇索引的数据和主键索引存储在一起。

14、事务的基本要素(ACID)

(1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没发生过一样。
例如:A账户中有1000元,B账户中有1000元。A要给B转账500元。A扣款和B加款这两条要么同时执行,要么同时不执行。如果在A扣款后B加款之前,系统发生故障,会回滚到A扣款之前的状态。
(2)一致性:事务开始之前和事务结束后,数据库的完整性约束没有被破坏。
例如:不论汇款成败,A账户B账户总额是2000元。
(3)隔离性:事务的执行互不干扰。
(4)持久性:事务执行成功后,该事务对数据库的更改是持久保存在数据库中的,不会被回滚。
可以使用日志记录或影子副本来实现。

15、什么是事务?

事务就是被绑定在一起作为一个逻辑工作单元的SQL语句分组
如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。
为了确保要么执行,要么不执行,就可以使用事务。
要将有组语句作为事务考虑,就需要通过ACID测试:
即原子性,一致性,隔离性和持久性。

- 锁:锁是实现事务的关键,锁可以保证事务的完整性和并发性。 与现实生活中锁一样,它可以使某些数据的拥有者,在某段时间内不能使用某些数据或数据结构。
老李 给 老王汇钱
老李把钱 ==》 银行 老李账户扣钱
银行 ==》 老王 成功 老王账户加钱
不成功 老账户补钱(银行将钱返给老李)

16、事务的并发问题

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

17、事务隔离性的作用

就是保证数据的一致性、完整性。
事务隔离级别越高,在并发下会产生的问题就越少,
但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。
所以设立了几种事务隔离级别,以便让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中做补偿。

18、事务的隔离级别4个

事务隔离级别读未提交读已提交可重复读串行化
脏读
------------------------
不可重复读
------------------------
幻读

19、mysql中锁的分类

按操作分
	读锁(共享锁)
		加了读锁,   其他的进程也可以进行读操作,但写操作会阻塞,所以称为共享锁
	写锁(排它锁)
		加了写锁,  其他的进程读操作和写操作都会进入阻塞状态	
按粒度分
	表锁
		加锁特点:开销小、加锁快,不会出现死锁;锁粒度大,锁冲突高,并发低
		加锁方式:
			lock table tableName read; //读锁
			lock table tableName write; //写锁
		解锁方式:
			unlock tables;//释放全部锁
	行锁
		开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 
		加锁方式:
			select * from table where id=1 lock in share mode; //读锁
			select * from table where id=1 for update; //写锁
			
		解锁方式:
        	commit; //提交事务即解锁
	页锁
		介于上面两个之间,不用特意阐述
从思想的层面:
	悲观锁:
	看待事情比较悲观, 认为别人会修改它的数据,需要上锁来保证数据的安全
	select * from employee where id = 1 for update
	update -- 
	
	
	乐观锁:
	看待事情比较乐观, 
	
	id   name  salary version 
	1     老王   1000     2
	
   客户端1   
   select * from employee where id = 1    版本 = 1   
   update employee set salary=2000,version=version+1 where id=1 and version = 1
   
   
   客户端2   
   select * from employee where id = 1    版本 = 1  2
   // 修改时1.一定要给版本号+1    2.条件中一定要有版本条件 
   update employee set salary=1000,version=version+1 where id=1 and version = 1
   
   
   读操作多 选乐观锁
   写操作多 选悲观锁

20、mysql中的几种连接查询

内连接:只有两个元素表相匹配的才能在结果集中显示。
	inner join
	
	select * from 表1,表2 where 表1.id = 表2.dep_id
	select * from 表1 inner join 表2 on 表1.id = 表2.dep_id
外连接:
    左外连接:左边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
    	left join
	右外连接:右边为驱动表,驱动表的数据全部显示,匹配表的不匹配的不会显示。
		right join
	全外连接:连接的表中不匹配的数据全部会显示出来。
	 	full join

21、sql的书写顺序和执行顺序

-- 编写顺序
select  distinct  查询字段
from  表名
JOIN 表名
ON  连接条件
where 查询条件
group by 分组字段
having 分组后条件
order by  排序条件
limit 查询起始位置, 查询条数

-- 执行顺序
from  表名
ON  连接条件
JOIN 表名
where 查询条件
group by 分组字段
having 分组后条件
select  distinct  查询字段
order by  排序条件
limit 查询起始位置, 查询条数

21、mysql优化综合性

1.表的设计优化
选择表合适存储引擎:
myisam: 应用时以读和插入操作为主,只有少量的更新和删除,并且对事务的完整性,并发性要求不是很高的。
Innodb: 事务处理,以及并发条件下要求数据的一致性。除了插入和查询外,包括很多的更新和删除。
尽量 设计 所有字段都得有默认值,尽量避免null。
数据库表设计时候更小的占磁盘空间尽可能使用更小的整数类型.

但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。
tinyint   int   bigint
因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,
在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,
 CHAR (255)            VARCHAR (255)    10  
 定长                   可变长度
 
 
 
 
100101
甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,
我们应该使用TINYINT而不是BIGINT来定义整型字段。
应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理, 而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。



2.索引优化
表的主键、外键必须有索引;
数据量大的表应该有索引;
经常与其他表进行连接的表,在连接字段上应该建立索引;
经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
索引应该建在选择性高的字段上; (sex 性别这种就不适合)
索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
频繁进行数据操作的表,不要建立太多的索引;
删除无用的索引,避免对执行计划造成负面影响;
表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大。


3.sql语句优化
SELECT语句务必指明字段名称(避免直接使用select * )
SQL语句要避免造成索引失效的写法
SQL语句中IN包含的值不应过多
当只需要一条数据的时候,使用limit 1
如果排序字段没有用到索引,就尽量少排序
如果限制条件中其他字段没有索引,尽量少用or
尽量用union all代替union
避免在where子句中对字段进行null值判断
不建议使用%前缀模糊查询
避免在where子句中对字段进行表达式操作
Join优化 能用innerjoin 就不用left join right join,如必须使用 一定要已小表为驱动


4.缓存优化 (数据库自身缓存  redis缓存 等等 )
为了提高查询速度,我们可以通过不同的方式去缓存我们的结果从而提高响应效率。

数据库本身也是支持缓存的 --> 查询缓存query_cache , 默认查询缓存是关闭的
需要我们在mysql.ini 配置文件中开启:
开启方法: 
query_cache_type=0   #关闭查询缓存
query_cache_type=1   #开启查询缓存,mysql自动帮我们缓存满足条件的查询数据
query_cache_type=2   #开启查询缓存,需要在参数中手动指定要缓存的查询

不过因为我们的课程体系主要讲解的是redis,所以在这里可以引入redis的知识点。



5.主从复制、读写分离
如果数据库的使用场景读的操作比较的时候,为了避免写的操作所造成的性能影响 可以采用读写分离的架构,读写分离,解决的是,数据库的写入,影响了查询的效率。读写分离的基本原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。 数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
参考: https://www.jianshu.com/p/faf0127f1cb2


6.mysql的分库分表   
数据量越来越大时,单体数据库无法满足要求,可以考虑分库分表
两种拆分方案:
垂直拆分:(分库)
业务表太多? 将业务细化 不同的小业务专门用一个库来维护
水平拆分:(分表)
单个表存的数据太多,装不下了? 将该表查分成多个

分库分表经常使用的数据库中间件:
MyCat
https://www.cnblogs.com/chongaizhen/p/11083226.html
Sharding-JDBC
https://blog.csdn.net/forezp/article/details/94343671

SpringCloud

【1】Spring Boot中的监视器

Spring Boot actuator

GET ip:port/actuator/health 报告应用程序的健康指标

GET ip:port/actuator/beans 描述应用程序上下文里全部的Bean

GET ip:port/actuator/env 获取全部环境属性

GET ip:port/configprops 描述配置属性(包含默认值)如何注入Bean

GET ip:port/env/{name} 根据名称获取特定的环境属性值

GET ip:port/info 获取应用程序的定制信息,这些信息由info打头的属性提供

GET ip:port/mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系

GET ip:port/metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数

GET ip:port/metrics/{name} 报告指定名称的应用程序度量值

POST ip:port/shutdown 关闭应用程序,要求endpoints.shutdown.enabled设置为true

GET ip:port/trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)

【2】什么是微服务

以前的模式是 所有的代码在同一个工程中 部署在同一个服务器中 同一个项目的不同模块不同功能互相抢占资源,微服务 将工程根据不同的业务规则拆分成不同的服务 服务部署在不同的机器上 服务之间通过远程相互调用

优点:

1、每个服务直接足够内聚,代码容易理解
2、开发效率高,一个服务只做一件事
3、易于第三方集成
4、解耦、易扩展

缺点:

1、开发人员要处理分布式系统的复杂性
2、多服务运维难度,随着服务的增加,运维的压力也在增大
3、服务间通讯成本
4、数据一致性
5、分布式链路跟踪问题

6、微服务框架代码侵入业务代码太强

【3】什么是spring cloud

简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、服务调用等组件,Spring Cloud是基于Spring Boot来开发的,目前国内主流的是Spring Cloud Netflix,还有最近由阿里新起的Spring Cloud Alibaba等,我们面试也是以Spring Cloud Netflix为主

【4】Spring Cloud Netflix有哪些组件

eureka (提供服务注册与发现功能)
ribbon(提供负载均衡功能)
Feign(内部整合了ribbon和Hystrix,具有负载均衡和熔断限流等功能)
Hystrix (提供了熔断限流等功能)
Zuul (提供了智能路由的功能)
spring cloud config (提供了统一配置的功能)

【5】SpringCloud如何服务注册发现

服务注册:服务在发布时会把将服务信息(IP地址和端口等) 注册到注册中心

服务发现: 只需要在main方法添加@EnableDiscoveryClient就可以了 默认会跟Ribbon组合使用

【6】Ribbon和Feign的区别

Ribbon和Feign都是用于调用其他服务的,不过方式不同。
1.启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。
2.服务的指定位置不同,Ribbon是在@RibbonClient注解声明启动类上,Feign则是在定义方法的接口中使用
3.调用方式不同,Ribbon使用RestTemplate发送给其他服务,
Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成方法即可,不过要注意的是方法的注解、方法参数要和提供服务的方法完全一致。

@EnableFeignClients(clients = UserService.class)
@RibbonClient(value="user-server")

【7】springcloud断路器的作用

当一个服务调用另一个服务由于网络原因或者自身原因出现问题时 ,调用者就会等待被调用者的响应, 当更多的服务请求到这些资源时,导致更多的请求等待 这样就会发生连锁效应(雪崩效应) 断路器就是解决这一问题

【8】spring cloud 和dubbo区别

1、服务调用方式: dubbo是RPC ,springcloud是 Rest Api
2、dubbo注册中心是zookeeper springcloud默认是eureka,也可以是zookeeper、Consul
3、springcloud服务组件更加丰富 比如:网关、断路器等等

【9】REST 和RPC对比

比较项RESTRPC
通信协议HTTP比如dubbo默认用的是dubbo协议
性能
灵活度

【10】负载均衡是什么

负载均衡适用于分布式环境中,主要优化资源使用,最大吞吐量,避免任何单一资源的过载

【11】微服务之间是如何独立通讯的

1.远程调用,比如feign调用,RPC调用
2.消息中间件

【12】什么是CAP理论

C——数据一致性,A——服务可用性,P——服务对网络分区故障的容错性

【13】Eureka和Zookeeper区别

1、Eureka取CAP的AP,注重可用性,Zookeeper取CAP的CP注重一致性。
2、Zookeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但选举期间不可用。
3、eureka的自我保护机制,会导致一个结果就是不会再从注册列表移除因长时间没收到心跳而过期的服务
4、Zookeeper有Leader和Follower角色、observer角色,Eureka各个节点平等。

【14】eureka自我保护机制是什么

当Eureka Server 节点在短时间内丢失了过多实例的连接时(比如网络故障或频繁启动关闭客户端)节点会进入自我保护模式,保护期间所有服务不会被删除,故障恢复时,自动退出自我保护模式。

【15】为什么要使用服务保障

例如此时数据库压力太大速度很慢,此时还有不断的请求访问后台,就会造成数据库崩溃

【16】什么是服务熔断/什么是服务降级

服务熔断:比如100个请求,有90个是异常请求,那么剩下10请求直接熔断,触发降级
服务降级:让其他请求快速结束,返回fallback的默认值

【17】Hystrix隔离策略

线程池隔离 和 信号量隔离

默认情况下,Hystrix 使用线程池模式

线程池信号量
开销排队、调度、上下文切换开销无线程切换,开销低
异步支持不支持
并发支持支持(最大线程池大小)支持(最大信号量上限)

【18】Hystrix缓存机制

在执行HystrixCommand命令的时候会判断commandKey存不存在,如果存在的话就认为是同一个请求结果直接返回,不存在的话才走整个请求流程

【19】什么是Ribbon

ribbon是一个负载均衡客户端,feign/eureka默认集成了ribbon

【20】Ribbon有哪些负载均衡算法

1、RoundRobinRule(轮询算法)

2、RandomRule(随机算法)

3、AvailabilityFilteringRule():会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量

过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问

4、BestAviableRule():会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

5、WeightedResponseTimeRule():根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高

6、ZoneAvoidanceRule():默认规则,符合判断server所在区域的性能和server的可用性选择服务器

【21】Ribbon缓存机制

ribbon自身也是有30s缓存的,所以ribbon会每30s向Eureka客户端那里索要服务信息

好处:

减少了对注册中心的压力

缺点:

这样就更加剧了整个系统对服务上下线感知的延迟

【22】Ribbon重试机制

ribbon实现了负载均衡,如果访问某服务的A节点超时后,会触发ribbon的重试机制

如何配置:

## 全局
ribbon:
	ReadTimeout: 6000
	ConnectTimeout: 6000
	MaxAutoRetries: 1
	MaxAutoRetriesNextServer: 2
## 局部
service-id:
    ribbon:
	    ReadTimeout: 6000
	    ConnectTimeout: 6000
	    MaxAutoRetries: 1
	    MaxAutoRetriesNextServer: 2

ribbon.MaxAutoRetries 设置为1,请求某服务6s超时后准备重试,该重试策略会先尝试再访问该实例,如果失败1次之后才更换实例访问。
ribbon.MaxAutoRetriesNextServer 决定了尝试更换2次实例。

【23】什么是feigin?它的优点是什么

1.feign采用的是基于接口的注解
2.feign整合了ribbon,具有负载均衡的能力
3.整合了Hystrix,具有熔断的能力

怎么使用的:
1.添加pom依赖。
2.启动类添加@EnableFeignClients
3.定义一个接口@FeignClient(name=“xxx”)指定调用哪个服务

feign的重试与ribbon存在冲突,因为feign默认集成Ribbon,所以默认是关闭feign自身的重试机制

【24】为什么用SpringCloudConfig

在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件

优点:

1、统一配置的增删改查
2、不同环境配置隔离(开发、测试、线上)
3、高性能、高可用性
4、请求量多、高并发

【25】SpringCloudConfig配置仓库是什么

默认使用的是git配置仓库、支持数据库等等

【26】为什么要网关服务

1、统一接收一切外界请求,转发到后端的微服务上去

2、服务网关中可以完成一系列的横切功能权限校验、限流、验签等等

【27】在Spring cloud中,能够使用的网关服务主要有哪些

Spring Cloud Gateway

Zuul

【28】Spring Cloud Gateway原理

1、Spring Cloud Gateway基于Netty异步非阻塞式的API

2、Gateway 工作原理 跟zuul的差不多,但是的 Filter只有 pre 和 post 两种,pre 过滤器逻辑先执行,执行代理请求;代理请求完成后,执行 post 过滤器逻辑。

【29】Spring Cloud Gateway跟Zuul的区别

网关组件基于框架阻塞模型容器
Zuul1.xServlet多线程阻塞模型Tomcat
Spring Cloud GatewaySpring 5.+响应式的、非阻塞式模型Netty

如何选择:

看公司业务需求Zuul的性能一般可以满足中小公司的业务,一台zuul的QPS一般可以到300以上

并发面试题

简述线程、进程、程序的基本概念?

进程 我们把运行中的程序叫做进程,每个进程都会占用内存与CPU资源,进程与进程之间互相独立.
线程 线程就是进程中的一个执行单元,负责当前进程中程序的执行。一个进程可以包含多个线程。一个进程包含了多个线程就是多线程。多线程可以提高程序的并行运行效率。是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。

创建线程的几种方法

1.继承Thread类。2.实现Runnable接口。3.使用匿名内部类。4.实现Callable接口

线程有什么优缺点?

优点:
1. 在多核CPU中,通过并行计算提高程序性能. 比如一个方法的执行比较耗时,现在把这个方法逻辑拆分,分为若干个线程并发执行,提高程序效率。
2. 可以解决网络等待、io响应导致的耗时问题。
3. 可以随时停止任务
4. 可以分别设置各个任务的优先级以优化性能
5. 提高CPU的使用率.提高网络资源的利用率

缺点:
1. 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
2. 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
3. 多线程存在上下文切换问题
CPU 通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就需要进行上下文切换,本身就会占用cpu资源。

start 和 run 方法有什么区别?

调用start方法方可启动线程,而run方法只是thread类中的一个普通方法调用,还是在主线程里执行。

可以直接调用Thread类的run()方法么

当然可以,但是如果调用了Thread的run()方法,它的行为就会和普通的方法一样,为了在新的线程中执行代码,必须使用Thread.start()方法。

Thread类的 sleep 方法和对象的 wait 方法都可以让线程暂停执行,它们有什么区别?

1、sleep()方法是属于Thread类中的,而wait()方法,则是属于Object类中的。

2、sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。所以在调用sleep()方法的过程中,线程不会释放对象锁。

3、调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

notify 和 notifyAll 有什么区别?

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll 方法。

sleep、join、yield 方法有什么区别?

t.join()方法会使主线程进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。t.join()中的t优先执行,当t执行完后才会执行其他线程。能够使得线程之间的并行执行变成串行执行。
线程调用了join方法,那么就要一直运行到该线程运行结束,才会运行其他进程. 这样可以控制线程执行顺序。

thread.yield()  让CPU的时间片尽量切换其他线程去执行

两个线程之间是如何通信的呢?

wait,notify等机制
synchronized
volatile

thread.join(), object.wait(), object.notify(), CountdownLatch, CyclicBarrier, FutureTask, Callable 等。

线程生命周期(几种状态)

线程的状态以及状态之间的相互转换:  
**1、新建状态(New):**新创建了一个线程对象。   
**2、就绪状态(Runnable)**:线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。   
**3、运行状态(Running):**就绪状态的线程获取了CPU,执行程序代码。   
**4、阻塞状态(Blocked):**阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
**5、死亡状态(Dead):**线程执行完了或者因异常退出了run()方法,该线程结束生命周期。等待被销毁。

为什么代码会重排序?

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

介绍一下java中的线程池

java线程池的工作原理和数据库连接池的差不多,因为每次重新创建线程 都是很耗资源的操作,所以我们可以建立一个线程池,这样当需要用到线程 进行某些操作时,就可以直接去线程池里面找到空闲的线程,这样就可以直接 使用,而不用等到用到的时候再去创建,用完之后可以把该线程重新放入线程池 供其他请求使用从而提高应用程序的性能。

用户线程和守护线程有什么区别

当我们在Java程序中创建一个线程,
它就被称为用户线程。
一个守护线程是在后台执行并且不会阻止JVM终止的线程。
当没有用户线程在运行的时候,JVM关闭程序并且退出。
一个守护线程创建的子线程依然是守护线程
比如说我们java中的GC回收 就是一个守护线程

你对线程优先级的理解是什么?

每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级

线程池的优点

重用线程池中的线程,减少因对象创建,销毁所带来的性能开销;
能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞;
能够多线程进行简单的管理,使线程的使用简单、高效。

线程池框架Executor

java中的线程池是通过Executor框架实现的,
Executor 框架包括类:
Executor,Executors,ExecutorService,
ThreadPoolExecutor ,
Callable和Future、FutureTask的使用等
Executor: 所有线程池的接口,只有一个方法。
ExecutorService: 增加Executor的行为,是Executor实现类的最直接接口。
Executors: 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口。
ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的
corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过 corePoolSize 个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时 核心线程数就是0,此时keepAliveTime控制所有线程的超时时间。
maximumPoolSize:线程池允许的最大线程数;
keepAliveTime: 指的是空闲线程结束的超时时间;
unit :是一个枚举,表示 keepAliveTime 的单位;
workQueue:表示存放任务的BlockingQueue Runnable队列。
BlockingQueue:阻塞队列(BlockingQueue)是java.util.concurrent下的主要用来控制线程同步的工具。

Java关键字volatile与synchronized作用与区别?

1,volatile 它所修饰的变量不保留拷贝,直接访问主内存中的。 在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为 了性 能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间, 在一个线 程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。一 个变量 声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory 中。
2, synchronized 当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代 码。 -、当两个并发线程访问同一个对象object中的这个synchronized( this)同步代码块时 ,一个 时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块 二、 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线 程仍然 可以访问该object中的非synchronized (this)同步代码块。
3、 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时, 其他线 程对object中所有其它synchronized (this)同步代码块的访问将被阻塞。
4、当一个线程访问object的一个synchronized (this)同步代码块时,它就获得了这个 object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
5、以上规则对其它对象锁同样适用.

同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象
(当然你也可以让它锁住整个对象)。
同步方法会 锁住整 个对象,哪怕这个类中有多个不相关联的同步块,
这通常会导致他们停止执行并需要等待获得这个对象上

Lock和synchronized

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

并发安全之volatile

当我们使用volatile关键字去修饰变量的时候,

所以线程都会直接读取该变量并且不缓存它。

这就确保了线程读取到的变量是同内存中是一致的。

并发安全之synchronized

在多线程并发访问资源(这类资源称为临街资源)的时候,由于割裂来了原子操作,所以会导致数据不一致的情况。为了避免这种情况,需要使用同步机制,同步机制能够保证多线程并发访问数据的时候不会出现数据不一致的情况。

一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁的线程释放锁。在Java中,每个类都有一个内置锁,之所以如此,是因为Java并发专家认为这样可以避免显式创建锁。

并发安全之Lock

Lock是java在1.5以后提供的线程安全接口,里面定义了几个方法:
如:
lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的
Lock lock = ...;
if(lock.tryLock()) { //获取锁
try{
//处理任务
}catch(Exception ex){
 }finally{
     lock.unlock();   //释放锁
 } 


}else {
//如果不能获取锁,则直接做其他事情
}
使用Lock必须要求我们手动释放锁。 (lock.unlock()方法)

不然可能造成死锁。

不可变对象对多线程有什么帮助

前面有提到过的一个问题,

不可变对象保证了对象的内存可见性,

对不可变对象的读取不需要进行额外的同步手段,

提升了代码执行效率。 前面有提到过的一个问题,

不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,

提升了代码执行效率。 前面有提到过的一个问题,不可变对象保证了对象的内存可见性,

对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。

volatile关键字在Java中有什么作用?

当我们使用volatile关键字去修饰变量的时候,

所以线程都会直接读取该变量并且不缓存它。

这就确保了线程读取到的变量是同内存中是一致的。

同步方法和同步块

同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。

请知道一条原则:同步的范围越小越好。

借着这一条,我额外提一点,虽说同步的范围越少越好,但是在Java虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大。

这是有用的,比方说StringBuffer,它是一个线程安全的类,自然最常用的append()方法是一个同步方法,我们写代码的时候会反复append字符串,

这意味着要进行反复的加锁->解锁,这对性能不利,因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,因此Java虚拟机会将多次append方法调用的代码进行一个锁粗化的操作,将多次的append的操作扩展到append方法的头尾,变成一个大的同步块,这样就减少了加锁-->解锁的次数,有效地提升了代码执行的效率。

常见线程名词解释

主线程:JVM调用程序main()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。

线程类的一些常用方法:

sleep(): 强迫一个线程睡眠N毫秒。   

isAlive(): 判断一个线程是否存活。   

join(): 等待线程终止。   

activeCount(): 程序中活跃的线程数。   

enumerate(): 枚举程序中的线程。

currentThread(): 得到当前线程。   

isDaemon(): 一个线程是否为守护线程。   

setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)   

setName(): 为线程设置一个名称。   

wait(): 强迫一个线程等待。   

notify(): 通知一个线程继续运行。   

setPriority(): 设置一个线程的优先级。

什么是死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。 当两个线程相互调用 Thread.join () 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁

线程调度器(Thread Scheduler)和时间分片(Time Slicing)

线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。 时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值