如何计算wait/compute,线下分析可以用profiler,线上用arthas,分布式系统可以使用链路追踪
线程创建原则一个不要:不要在构造方法启动线程(指令重排,this 溢出)
synchronized关键字保证可见性和原子性,不保证有序性
race condition => 竞争条件 , 指的是多个线程访问共享数据的时候产生竞争
数据的不一致(unconsistency),并发访问之下产生的不期望出现的结果
如何保障数据一致呢?--> 线程同步(线程执行的顺序安排好),
monitor (管程) ---> 锁
critical section -> 临界区
如果临界区执行时间长,语句多,叫做 锁的粒度比较粗,反之,就是锁的粒度比较细
具体: 保障操作的原子性(Atomicity)
悲观的认为这个操作会被别的线程打断(悲观锁)synchronized(上一个小程序)
乐观的认为这个做不会被别的线程打断(乐观锁 自旋锁 无锁)cas操作CAS = Compare And Set/Swap/Exchange
字节码层面:monitorEnter ,monitorExit
synchronized锁升级synchronized锁有四种状态,无锁,偏向锁,轻量级锁,重量级锁,这几个状态会随着竞争状态逐渐升级,锁可以升级但不能降级,但是偏向锁状态可以被重置为无锁状态
偏向锁
为什么要引入偏向锁?
因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。
偏向锁原理和升级过程
当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。
轻量级锁
为什么要引入轻量级锁?
轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。
轻量级锁原理和升级过程
线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址;
如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。 自旋锁简单来说就是让线程2在循环中不断CAS
何时升级为重量级锁?
但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,有线程自旋超过十次,或着自旋的线程数超过cpu核数的一半.jdk6以后,加入自适应自旋,adapative self spinning ,由jvm自己控制何时升级为重量级锁> 那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。
为什么有自旋锁还用重量级锁 偏向锁一定比自旋锁效率高? 为什么synchronized必须是可重入锁
同一个线程锁方法1调用锁方法2,再调用2的时候也要获取锁(同一把锁,同一个锁对象),如果此时不能可重入就不能调用了
如何控制线程执行顺序Thread中,join()方法 - knightsoul - 博客园
常见多线程面试题之Thread的join()方法_2的32次方-CSDN博客_thread.join
Reentrant和synchronized的区别使用注意 try finally
底层是 cas
百度安全验证
ReadWrite的使用场景一道面试题比较synchronized和读写锁 - V青山绿水 - 博客园
例如黑白名单筛选 (读取大量的号码,检查是否是黑名单号码,如果是,开始写操作())
线程间的通信问题 控制线程的交替执行几种方法
1.Collections.synchrnized()
2.两次 wait notify
3.两次countdownlatch
4.两次LockSupport park unpark
5.Semaphore 加join
顺序交替打印问题编写一个程序,创建两个线程,要求分别输出26个字母,在输出结果时,要显示哪个线程输出的字母。? - 知乎
经典面试题:两个线程交替打印数字和字符 - 灰信网(软件开发博客聚合)
生产者和消费者问题一篇文章,让你彻底弄懂生产者--消费者问题 - 简书
ThreadLocal原理声明一个threadlocal变量的时候,先看他的set方法
set方法的实质是将传入的值存入一个map中,而这个map是属于当前线程的
也就是说其他的线程获取不到, 当调用get方法的时候,只能获取到的是本线程里面的值
threadlocal与弱引用 如何避免内存泄漏 强软弱虚引用的特点 ,一般用在什么地方softRefenerce 一般用做缓存 ,内存不足就清理掉
weakRefenerce 一个典型应用就是应用在threadlocal
phantomRefenerce 特点: 获取不到值,被gc的对象存到一个map集合中,可用于操作直接内存
线程池相关如何正确的创建线程池
线程池的几个参数
如何自定义线程名称
如何自定义拒绝策略
为什么不建议用Executors
常用的workQueue有哪些?
线程池的工作队列 - guoyu1 - 博客园
forkjoinpool相关
关键类,实现方法 逻辑 任务拆分,可以指定拆分逻辑,pool执行
介绍 ForkJoinPool 的适用场景,实现原理_大大肉包博客-CSDN博客_forkjoinpool



