【JUC】并发编程的艺术笔记
1.单核处理器也支持多线程执行代码,使用CPU分配时间片来实现。
2.上下文切换:任务从保存到再加载的过程。
3.减少上下文切换(避免频繁切换任务):
1)无锁并发编程。
2)CAS算法。
3)使用最少线程,避免创建不需要的线程。
4)使用协程:单线程里实现多任务的调度。
4.volatile轻量级的synchronized,保证了共享变量的可见性。
5.线程在将volatile共享变量写回共享内存时,会有lock指令,通过MESI总线嗅探机制,每个线程嗅探总线上的变量根据缓存一致性协议,使他们工作空间的变量无效,必须重新去共享内存再一个取。
6.volatile优化:将变量填充字节码到64字节,处理器的cache高速缓存行都是64字节,使用linkedTransferQueue定义了头节点和尾结点用来放置和拿取,填充字节码到64之后保证头尾结点 不会被同一高速缓存行锁定。
7.sychronized重量级锁,使用方法:
1)锁普通同步方法,锁当前实例对象
2)锁静态同步方法,锁当前类的Class对象。
3)同步方法块,锁括号里指定的对象。
8.JVM基于进入和退出Monitor对象来实现同步,字节码monitorexit和monitorenter指令 是sychronized底层实现,字节码底层是操作系统的mutexLock互斥锁,因为Java的线程模型是和OS一一对映的,所以每次mutexLock都要引起 用户态内核态的切换 。每一个对象都有自己的监视器。
9.Java的对象头(1,Mark Word 2,指向类的指针 3,数组长度(只有数组对象才有))
mark word无锁条件下存储:hashcode 分代年龄 锁标记位。
mark word对应锁的状态变化
10.对象的结构(对象头 对象实例 填充字段)
11.无锁:两种情况:不存在资源竞争,存在竞争但是只允许一个线程修改(CAS)实现无锁编程
12.偏向锁:使用mark word来标识线程ID。
运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
13.轻量级锁:
1)其他线程竞争对象,会自旋(CPU轮询访问是否解锁了)。
2)适应性自旋:根据上一个在锁上的自旋和锁状态 确定自旋时间。
3)外面竞争的资源超过1个,升级为重量级锁。
14.锁只能升级 不能降级。
15.处理器保持原子操作:通过缓存、通过总线加锁。
16.Java使用锁、循环CAS来保证原子性。
17.CAS比较与替换,CAS使用处理器层面的指令完成原子操作。
cas以及ABA问题:https://www.cnblogs.com/cckong/p/14444374.html
18.悲观锁:操作系统认为如果不严格同步线程调用,就一定会出异常。所以只提供一个线程调用,而阻塞其他线程。
乐观锁:操作系统认为不会被其他线程更改,所以不需要加锁。、
19.运行Java程序时,启动一个Java进程,在进程中有很多线程比如用户线程main、守护线程gc等。
20.设置线程优先级,但是具体还是要由操作系统决定。
21.线程状态以及变化过程
22.守护线程,守护线程的run方法代码块里不一定全部执行,因为JVM可能比执行更早退出。
23.线程的中断机制:
1)interrupt():设置中断标识位,正在运行的线程无法中断,在wait、sleep、jion的线程终止阻塞,抛出异常。
2)isInterrupted():方法唯一的作用只是测试线程是否已经中断,中断标识位的状态并不受到该方法的影响。
3)interrupted():方法的作用是测试当前线程是否已经中断,线程的中断标识位由该方法清除。
24.sychronized竞争资源图
25.sychronized加等待/通知机制的流程图
wait/notify必须是同步块、同步方法里面的。
调用wait方法,进入等待队列,状态也变为等待状态。
调用notify方法,并不会马上放弃锁,要等运行完,才释放。
notify是让一个线程从等待队列到同步队列,nitufyAll是让所以线程从等待队列到同步队列。
同步队列上的线程状态为Blocked阻塞态。
26.jion方法 让调用此方法的线程 插队执行。
27.ThreadLocal 线程变量,以ThreadLocal对象为键,任意对象为值得储存结构(ThreadLocalMap类型)。
应用:spring使用threadLocal保证单个线程中得数据库连接是同一个。
28.锁 lock 提供了比synchronized更加灵活的加锁方式
29.AQS队列同步器:构建锁以及其他同步组件的基础框架。同步器可以独占式也可以共享式获得同步状态。
使用int变量来表示同步状态,以及内置的FIFO队列来完成线程的排队。
重写同步器方法时需要用到 下面来访问、改变同步状态
getState();//获得同步状态 setState(int newStare);//设置当前同步状态 compareAndSet(int expect,int update);//CAS设置当前状态