CSDN个人博客:https://blog.csdn.net/wangrain1
1. t.yield() ==》 t1回到就绪状态,所有线程重新抢用cpu。
2. t2正在运行,这时在t2线程中调用 t1.join() ,这是让t2 等待线程t1 结束了,再运行。次方法可以让线程按顺序持行。自己线程中调用自己的join方法是无意义的。
3. 线程运行状态
图片分析:1. new 的状态
2. start() 之后线程到达就绪状态等待cpu调度,cpu调度了 就是运行状态。
3. 调用了 TImedWaiting 就回到就绪状态,结束之后自动到运行状态
4. 调用 Waiting 就绪然后持行相应的方法到运行
5. 调用 Blocked 等待进入同步代码快的锁。
6. 结束状态Teminated
线程被挂起:正在cpu中调度的线程,cpu把该线程丢出去,持行其他线程,这就是线程被挂起。之后又回来持续该线程。(cpu每次只能持行一个线程)
4. synchronized 可重入锁(保证原子性和可见性)
class T {
pravite synchronized void m (){
mm(); // 同一个线程是可以调用的
}
privite synchronized void mm (){
}}
其实两个方法是同一把锁,锁的都是this对象。(同一个线程获得的锁是同一个锁,锁方法可以调锁方法,因为是同一个线程。如果是其他线程调用mm方法就不行,因为当前锁被m占用着。)
5. 程序中如果出现异常,默认情况下synchronized 锁是会被释放的。
6. synchronized 底层实现--》锁升级过程
1. 在早期jdk中synchronized是个重量级锁,每次加锁都需要向操作系统(OS)申请,代码运行效率非常低。
2. 后来对synchronized 锁做了升级:
1.当我们用synchronized 锁了一个Object对象之后,Object就会记录这个线程的id(叫做:偏向锁)
2.如果出现了其他线程争用这个锁,就会升级为(自旋锁:段时间自旋等待)
3.如果出现了大量的线程争用这个锁,这个锁就会升级为(重量级锁:向操作系统os申请)
自旋锁:synchronized/Atomic / lock 这些都是自旋锁。
对于何时使用自旋锁(jvm调优可以选择调节参数)何时选用重量级锁主要看你需要锁的 方法的运行时间,运行时间很长或者线程数量多的就用重量级锁(不在等待队列不占用cpu),否则就选用自旋锁(等待占用cpu)操作时间短。
自上而下效率逐渐降低 (参考:我就是厕所所长1/2)
7. wait()时候当前线程占有的锁就被释放了。但是程序是停在了当前wait()处,当notify()之后代码还在wait()处向下持行。
8. 什么是 CAS 呢?
9.锁的分类
锁的分类
10.volatile (可见性,有序行)
11. synchronized 在hotspot中的实现
1. synchronized 在字节码中的指令是moniterEnter 对应 moniterExit 表示进入和退出
2. 对象的创建过程(new的过程):JAVA对象创建的过程
三步:开辟空间创建属性对象赋默认值,调用构造方法赋初始值,给栈里的对象赋引用。(cpu指令重排提高效率)
3. 对象在内存中的存储布局
过程描述:一个对象创建之后包括四个部分如上图,其中markword对象头中保存了对象的hashCode,分代年龄,锁标志。
new出来的对象还是个无锁的状态,当有一个线程访问时候,该锁就升级成了偏向锁/这时的hashCode就被转移到了该对象的stack栈空间中,对象的markword中保存当前线程id等,此时偏向锁标志被标记为1,。如果这时出现了另外一个线程在等待此对象的锁,那么这个锁就升级成了轻量及锁(自旋锁:一直在for循环等待试探是否可以获取锁,消耗cpu)如果自旋超过10次,该锁就被升级为重量级锁,重量级锁是操作系统OS处理的,重量级锁会把当前等待的线程丢到等待队列中去,等锁释放了再从队列中拿出来。GC每回收一次分代年龄就会加1 当gc年龄到达6岁对象就升到老年代区域。
4. 单例的双重检测
对象需要加 volatile 防止指令重排序(需要了解对象创建过程)。
5. volatile的实现细节:内存屏障防止指令重排
6.LockSupport / CountDownLatch / ReenTrantLock+ Condition的使用
网友评论