Synchronized

要保证在一个时刻,有且只有一个线程在操作共享数据,此时便引入了互斥锁
synchronized 便满足了以上特征
synchronized 锁的不是代码,锁的是对象,这个一定要记住,特别是在区别 访问被锁方法和被锁同步代码块时候

是同一个类,不管是不是同一个对象都会被锁住,顺序执行。
两种缩是没有关系的,互不干扰的。
底层实现原理
hotspot 虚拟机
实例数据,对齐填充,需要自己看
对象头 不存和类定义相关的数据,所以markword存储不固定的长度数据,存储对象运行时数据,会根据对象状态复用自身空间

重量级锁,指向的就是monitor对象的起始地址,每个对象都存在一个monitor与之关联,这两个对象的关联,存在多种实现方式,一起创建销毁or当前线程获取对象锁时候自动生成。
在HotSpot 里面是靠ObjectMonitor 实现的,在JVM虚拟机源码里面,通过C++实现的,ObjectMonitor.cpp里面
owner 里面存线程拥有者,count存储有几个线程持有,每个Java对象都拥有一个ObjectMonitor对象
javap看class里面的编译的字节码,标红的位置分别是同步代码块的开始位置和结束位置,可以重入(本身调用本身)



最后对多一个exit ,为了异常处理,异常结束时候,为了释放,配对执行
方法同步是隐士的,如果抛出异常,并无法处理,异常并会被抛到同步方法外,自动释放资源
底层MutexLock 操作系统实现

自旋锁与自适应自旋锁
在多CPU之后考虑

白白消耗CPU资源,所以要限制自旋次数,采用传统方式,挂起线程了
可以通过参数设置自旋次数,但这个次数交给人设置,多不好啊,所以就有了自适应自旋锁
之前得到过,并且正在运行中,JVM认为获取可能性很大,就会自动增加自旋时间 50次,相反会时间越来越少
锁消除



锁粗化
同步块尽可能小,另一个极端,就扩大
粗化到while之外
锁升级
升级过程 需要看

偏向锁






解锁:
线程1当前拥有偏向锁对象,线程2是需要竞争到偏向锁。
线程2来竞争锁对象;
判断当前对象头是否是偏向锁;
判断拥有偏向锁的线程1是否还存在;
线程1不存在,直接设置偏向锁标识为0(线程1执行完毕后,不会主动去释放偏向锁);
使用cas替换偏向锁线程ID为线程2,锁不升级,仍为偏向锁;
线程1仍然存在,暂停线程1;
设置锁标志位为00(变为轻量级锁),偏向锁为0;
从线程1的空闲monitor record中读取一条,放至线程1的当前monitor record中;
更新mark word,将mark word指向线程1中monitor record的指针;
继续执行线程1的代码;
锁升级为轻量级锁;
线程2自旋来获取锁对象;

Synchronized 和ReentrantLock 区别


公平性会导致额外开销


Condition 将以上对象化
ArrayBlockingQueue 就是使用Condition

happens-before










volatile


不是线程安全的value++,所以必须sync修饰,


为何会立即可见




会拿到没有初始化的对象,就造成了线程安全问题
JVM并没有规定谁在前谁在后,那么就存在这种情况:线程A开始创建SingletonClass的实例,此时线程B调用了getInstance()方法,首先判断instance是否为null。按照我们上面所说的内存模型,A已经把instance指向了那块内存,只是还没有调用构造方法,因此B检测到instance不为null,于是直接把instance返回了——问题出现了,尽管instance不为null,但它并没有构造完成,就像一套房子已经给了你钥匙,但你并不能住进去,因为里面还没有收拾。此时,如果B在A将instance构造完成之前就是用了这个实例,程序就会出现错误了。
一定是volatile 的读写 happens-before 原则

使用volatile 避免步骤2和3的重排序
CAS


Unsafe 里面使用的CAS 是Native的

这就需要业务方面考虑是否要有ABA版本问题,会导致并发
Java 线程池


双端队列保存任务,工作线程从头,窃取线程从尾部




大部分业务使用给定的5类线程池,有些业务场景需要自己使用线程池
线程池的设计于实现



WorkerThead 被抽象成 Worker对象,继承自AQS
线程池处于shutdown状态,也需要策略去处理


如果阻塞队列满了,并且没有空闲线程,需要不同策略
thredFactory使用默认,会创建相同优先级的非守护线程


线程池记录线程状态优雅方式:



线程池大小如何决定
