知识点
死锁条件
必须同时满足:
- 互斥等待(意思是存在锁)
- hold and wait
- 循环等待
- 无法剥夺的等待
死锁防止
- 破除互斥等待–>一般无法破除
- 破除hold and wait–>一次性获取所有资源
- 破除循环等待–>按顺序获取资源
- 破除无法剥夺的等待–>加入超时
线程的状态
新建状态(New)
就绪状态(Runnable)
运行状态(Running)
阻塞状态(Blocked)
死亡状态(Dead)
一个很好的讲解https://blog.csdn.net/peter_teng/article/details/10197785#:~:text=%E7%BA%BF%E7%A8%8B%E4%BB%8E%E5%88%9B%E5%BB%BA%E3%80%81%E8%BF%90%E8%A1%8C%E5%88%B0,%E9%98%BB%E5%A1%9E%E7%8A%B6%E6%80%81%E5%8F%8A%E6%AD%BB%E4%BA%A1%E7%8A%B6%E6%80%81%E3%80%82
线程池
- 创建线程开销大
- 线程池:预先建立好线程,等待任务派发
- 线程池的参数:
corePoolSize:线程池中初始线程的数量,可能处于等待状态
maximumPoolSize:线程池中最大允许线程数量
KeepAliveTime:超出corePoolSize部分线程如果等待这些时间将被回收。
synchronised的底层实现
详细讲解:https://juejin.cn/post/6844903960826871815
同步代码块:采用monitorenter、monitorexit两个指令来实现同步
同步方法: 采用ACC_SYNCHRONIZED标记符来实现同步
ReentrantLock 比 synchronized的区别和相同点
相同点:都是可重入锁,一个线程可以多次获取同一个锁。
不同点
- 和synchronized不同的是,ReentrantLock可以尝试获取锁,下面述代码在尝试获取锁的时候,最多等待1秒。如果1秒后仍未获取到锁,tryLock()返回false,程序就可以做一些额外处理,而不是无限等待下去。。
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
...
} finally {
lock.unlock();
}
}
- ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
- synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。
volatile解决什么问题和应用场景
volatile实现了:
当一个变量被定义为volatile之后,它对所有的线程就具有了可见性,也就是说当一个线程修改了该变量的值,所有的其它线程都可以立即知道,可以从两个方面来理解这句话:1.线程对变量进行修改之后,要立刻回写到主内存。2.线程对变量读取的时候,要从主内存中读,而不是工作内存。
但是这并不意味着使用了volatile关键字的变量具有了线程安全性。
Volatile还可以用来禁止指令重排序。不允许该指令之后的所有操作不能重排序到该指令的前面。
应用场景:
一个变量,如果有多个线程只有一个线程会去修改这个变量,其它线程都只是读取该变量的值就可以使用Volatile关键字。
作者:夏昊
链接:https://www.zhihu.com/question/31990408/answer/830790165
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Java内存模型(Java Memory Model)
JMM是一种规范。
定义了:happens-before关系
一个很好的关于NIO的讲解
https://segmentfault.com/a/1190000017040893
Java垃圾回收
JVM结构(Java内存结构)
- Runtime Data Area: Method Area,Heap Memory,Java Stacks,PC Register,Native Method Stack.
- Execution Engine:JIT Complier, Garbage Collector
注:Native Method :java虚拟机和真实操作系统打交道的地方
JIT Complier把Java虚拟机的指令编译成真实运行的机器指令。
ClassLoader把class文件读进Java虚拟机。
Java 垃圾回收
如何确定垃圾
是采用引用计数吗? NO
原因:1. 增加了开销。2. 循环引用
确定方法:是否从垃圾回收的根可见,
垃圾回收的根就是Java虚拟机认为一定有用的东西,从一定有用的东西出发看见的东西都是有用的,看不见的就是垃圾。
什么是垃圾回收的根结点:
- 局部变量(触发垃圾回收时整个虚拟机的运行会暂停,暂停的这一刻有运行的线程,每个线程运行到一半的时候函数调用里就有很多局部变量)
- 静态变量
- Native 方法所引用的对象
- 活动线程(线程本身也是对象),等待中的Monitor(wait,notify,synchronized)
Java从垃圾回收的根结点遍历所有引用关系,所有看到的都是有用的,没看到的就是垃圾。
垃圾回收最朴素的算法开始推演到真正的Java垃圾回收算法
最朴素的算法:Mark and Sweep
Mark and Swap:把垃圾直接清除。缺点:内存碎片化严重,图中的每个方块内存大小为1,假如要放一个内存为2的对象会发现放不了。我们针对这点提出了改进算法Compact.
改进后每次回收会把剩余的对象排整齐了。缺点:假如一个内存为2的对象往前移动一个内存单位,就出现源与目的地的冲突。针对这点提出了Copy算法。
只用一半的内存,每次垃圾回收把内存一有用的东西拷贝到内存二里面去然后把内存一的东西丢掉。
Java垃圾回收算法把上述朴素的算法合起来,针对不同情况采用不同的算法。
Java分代垃圾回收算法
xind c 新生代:大部分对象只存在很短的时间,放在新生代,小部分存在很长时间的对象,放在老生代。新生代要经常进行垃圾回收,所有要优化性能,因此采用改进的copy算法。