加在方法上:是加上一个标记
加在代码块上:
有两个moniterexit:是为了万一有异常时也能释放锁。
wait():不仅会释放cpu的时间片,还会释放锁资源。
sleep():只会释放cpu时间片,而不会释放锁资源。
涉及到moniter机制,是属于object对象的。
synchronized的等待原理:
首先1,2同时进入Entry list去抢锁资源,1抢到后便占有锁资源,而2在Entry list上等待,如果1调用了wait()方法则会释放锁资源和cpu时间片,然后就在wait set上等待,2获取锁资源执行,执行完便离开,然后唤醒1.获取锁过程也是通过CAS去判断objectmoniter中Owner是否为null,然后尝试讲Owner设置为当前线程。
因为涉及到性能问题,所以就涉及到锁升级了。
CAS和自旋for(; 😉,自旋也会消耗性能,所以会设置自旋次数(自适应自旋),根据之前是否获取锁来定次数的,如果超过这个次数还获取不到,就自动升级到重量级锁。锁升级不会逆转。
如何判断是什么锁?
通过对象头的锁标志位判断。
还有年龄位:其实就是判断gc的年龄。
锁膨胀过程:
偏向锁->轻量级锁
轻量级锁->重量级锁
线程2一直自旋还是没获取到锁,则会被放在entry list队列中,阻塞,然后锁升级
CAS可以使我们的读写操作是原子的。(要聊到JMM模型)
ABA问题用时间戳解决。
2.两个队列
3.锁标志位
AQS涉及到的是state(volatile修饰,JMM)判断当前锁是否被占用。也是通过CAS来修改的。
AQS中的等待队列争抢锁资源不一定是从队列头取的,其中涉及到同步队列和条件队列。
JMM:
硬件架构问题:
多核会导致缓存不一致解决:
加锁:
总线加锁:加载到缓存时再加锁,回写完后才解锁,但这样多核就变成单核了,性能很差,基本不考虑。
锁缓存行:缓存一致性协议(MESI),只要一个线程中修改了数据,则总线嗅探机制就会通知其他核变成无效。
总线嗅探机制
线程池:
Thread只能单继承,Ruannble可以多实现,Callable接口可以有返回值。
接口可以多继承噢。
例子:生成10000个数加入到list
thread.join(),就是让mian线程一直等待10000条线程执行结束才结束,如果去掉join()则输出会小于10000,即没等10000条线程执行完就结束了。通常用在main线程上。就是让mian线程阻塞的。
Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。
线程池的方法:
对线程进行重复利用。
面试:submit和execute的区别
1.submit是有返回值的,而execute是没有返回值的
2.其实都是在执行ThreadPoolExecute里面的execute()方法。
Executor是我们线程池的顶级类。
AQS同步器:
线程池执行步骤:
1.先通过CAS去获取当前任务的状态和数量,如果任务数量小于corePoolSize,则直接创建线程去执行。
2.如果大于corePoolSize则放入等待队列中,然后再等待被调用。
3.如果队列满了,则会直接new一个非核心线程去执行任务。
4.如果达到最大的线程数量,则会执行拒绝策略。
队列
拒绝策略
1.拒绝抛异常(默认)AbortPolicy
2.直接丢弃
3.
4.