并发
synchronized
同一时刻只能一个线程执行代码块
可以修饰方法和代码块
如何实现可见性问题
实现可见性的过程
获取互斥锁
清空本地代码,将主内存中的最新拷贝到本地内存
将更改后共享变量值刷新到主内存
释放互斥锁
如何实现同步
都是使用mointorenter和monitorexit两个JVM指令实现
什么是管程
管理共享变量以及对共享变量操作过程,使得支持并发
线程可以对monitor执行lock和unlock操作进行加锁和释放锁
解决互斥问题的思路:将共享变量及对共享变量的操作统一封装起来
锁优化
同步锁的四种状态:无锁、偏向锁、轻量级锁、重量级锁
偏向锁:一个线程加锁
轻量级锁:两个线程交替自旋
同步锁锁定资源是对象
volatile
可见性:对变量的修改对所有线程可见
可见性
- volatile在写操作的时候,JVM会发一条lock前缀的指令,将这个缓存的变量会写到系统主存中;其他的使用的时候会从主存读取最新的数据。所以可见
内存屏障(Memory Barrier)是CPU的一种指令,用于控制特定条件下的重排序和内存可见性问题。
Java编译器会根据内存屏障的规则禁止重排序
为了保障volatile变量的可见性和禁止指令重排序,java在字节码中插入内存屏障实现
内存屏障解决指令重排
有序性:禁止指令重排,遵循happens-before原则
双重检验锁必须加volatile,因为内存屏障
双重校验锁实现一个单例
否则会出现空指针
存在问题
不满足原子性
解决方法
使用syn
使用可重入锁
使用原子类
与syn的区别
volatile不需要加锁,不会阻塞线程
volatile是一种简单的同步机制
JUC
锁的分类
CAS:一条CPU的原子指令,可以保证共享变量修改的原子性
使用unsafe类实现,其中都是native方法
根据内存偏移量找到待更新的原值的准确内存地址,使用compareAndSwaplant将待更新的值和预期值进行比较
CAS缺陷
循环时间太长
只能保证一个共享变量原子操作
ABA问题
- 解决方案:AtomicStampedReference(加个时间作为版本号)
问题:Thread 的join方法
与syn的比较
syn在以下情况下释放锁
线程执行完释放
线程执行时发生异常,JVM会自动释放
锁方法执行了wait方法,进行释放锁
syn的问题
无法控制阻塞时长——>JUC trylock()解决
阻塞不可中断——>lockInterruptibly解决
syn不支持读写锁分离——>JUC的ReentrantReadWriteLock锁
AQS
先进先出队列+CAS+volatile
- 维护一个volatile的int类型的state变量,state=1是获取到锁;state的值变化是由CAS完成的
实现
CAS操作提供原子性避免锁
volatile确保修改的可见性和内存操作的有序性
ThreadLocal
解决并发问题,在线程中传递数据
通过为每一个线程创建一份共享变量的副本保证各个线程之间的变量访问和修改互不影响
内存泄漏问题
为什么
key ThreadLocal的引用缘
栈上的ThreadLocal引用
ThreadLocalMap中的key对他的引用
value
- 引用只有一条,从Thread过来的引用
出现的问题
ThreadLocal栈上的引用不见了,但是Threadlocal对象因为还有一个引用,索引无法回收
- 解决方法:ThreadLocal的key改成弱引用
Thread对象一直被使用,无法释放
- 解决方法:对于value,Thread一直没有释放,只有在一个ThreadLocal用完的时候,手动调用一下remove方法
解决