- 什么是上下文切换
多线程同时处理多个任务,使用时间分片在任务之中进行切换,CPU循环执行分片任务,不断进行保存上一个任务状态,加载这个任务状态的过程。
\
- 多线程一定快吗
看并发量,数据量不大的时候,上下文切换引起的损耗,反而不如串行效率好。
所以需要进行并发量测试,以及使用Lmbench检测山下文切换时长,使用vmstat检测上下文切换次数。
\
-
减少上下文切换
-
无锁并发编程:多线程环境下,不使用锁,锁会频繁切换不同线程,数据ID进行Hash取模分段,不同线程处理不同段数据。
-
CAS:compare and swap,比较并且交换,Atomic包更新数据,不需要加锁。
-
最少线程:根据任务情况,减少线程数量。
-
协程:单线程中实现多任务调度和切换。
\
- 实战调优
1 jstack命令dump线程信息
2 发现线程处于什么状态
3 打开dump文件,分析出问题的线程
4 减少出问题的线程数量
5 重启并查看
\
- 什么是死锁
两个线程,相互持有对方需要的锁资源,并一直等待,就造成死锁。
\
-
避免死锁
-
避免一个线程获取多个锁
-
尽量保证一个锁占用一个资源
-
使用定时锁,tryLock(timeout),超时自动释放
-
数据库锁,加解锁在一个数据库连接里,否则可能出现解锁失败
\
- 资源限制是什么,怎么解决?
并发的时候受限于计算机硬件或软件资源,反而不如串行效率好。
硬件可以通过多机器运行解决这个问题,软件可以复用资源池,总之具体问题具体分析。
\
- 关于volatile
如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。
volatile变量写操作是有第二行汇编代码,lock前缀修饰:
1 将当前缓存行的数据写会到系统内存。(赋值)
缓存锁定,可以独占被Lock的共享内存,锁定这块内存区域的缓存并写回内存,缓存一致性保证原子性,只能同时修改一个处理器缓存的内存区域数据。(禁止多核同时)
2 使其他CPU里缓存了这个内存地址的数据无效。(那就必须重新读取缓存了呗)
处理器通过嗅探技术保证他的内部缓存、系统内存和其他处理器缓存数据,在总线上保持一致。
如果其他处理器打算写入一个共享内存的内存地址,那么嗅探的处理器使它的缓存行无效,下次访问相同的内存地址,就会强制执行缓存行填充。
\
- volatile缓存行填充
如果一个volatile对象被频繁写,那么,可以根据处理器位数(32或64),进行对象填充至处理器位数,这样,频繁写的时候,不会造成,并行的对象被锁到一个缓存行内。(知道就行,一般用不到)
\
- 关于transient
不能被序列化,一些无需离开JVM的变量,可以进行使用。
\
-
关于synchronized
-
普通同步方法,锁当前实例对象,对象锁
-
静态同步方法,锁当前CLass对象,类锁
-
同步方法块,括号里是this对象锁,是Class类锁
锁存在对象头里,32位和64位,存贮方式不一样,不同的锁,占用字节的方式也不一样。
四个级别:重量锁、轻量锁、偏向锁、无锁。锁等级只能升级不能降级。
-
偏向锁:适用于一个线程访问,发生竞争升级轻量锁。直接CAS持有锁之后,下次直接去取锁持有,解锁需要安全点释放。
-
轻量锁:CAS表头,每次CAS进行自旋获取锁,同步块不阻塞,使用就加锁,用完就解锁。多次竞争失败就升级重量锁。
-
重量锁:CAS表头,不用自旋,使用就加锁,用完唤醒其他阻塞线程。其他线程竞争失败就阻塞。
\
- 原子操作原理
定义:不可被中断的一个或一系列操作。