一 .**CPU
CPU怎么保证并发控制
(1) 关中断
(2)缓存一致性协议 每个硬件厂商 协议不同 常见的interl 的 MESI 通过四种状态 判断如上图
CPU的速度和内存的速度(100 :1)
这里的速度值得是ALU访问寄存器的速度比访问内存的速度快100倍
为了充分利用CPU的计算能力,在CPU和内存中间引入缓存的概念(工业上的妥协,考虑性价比)
现在的工业实践,多采用三级缓存的架构
缓存行:一次性读取的数据块
程序的局部性原理:空间局部性 时间局部性
如果缓存行大:命中率高,但读取效率低。如果缓存行小:命中率低,但读取效率高。
工业实践的妥协结果,目前(2021)的计算机多采用64bytes (64 * 8bit)为一行
由于缓存行的存在,我们必须有一种机制,来保证缓存数据的一致性,这种机制被称为缓存一致性协议。
(3)系统屏障
保证系统运行顺序 比如voliate
二. 内存模型
java 内存模型 JMM
Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
主内存: 共享资源
工作内存:copy 主内存
jVM 内存模型
堆 java 运行时的数据去,垃圾回收器负责回收,动态分配内存,垃圾回收器会定时收走不用内存
优点:动态分配内存大小
缺点:由于要在运行时动态分配,所以会存取速度慢 (扩容)
栈 () 存放引用地址大地方
优点:存储数据快,仅次于计算器cpu 寄存器,
缺点:大小固定 比如 8大基本类型
方法区 存储类信息、常量、静态变量、即时编译器编译后的代码。
程序计数器 程序计数器,线程私有、指向下一条要很执行的指令
本地方法区 为虚拟机使用到的Native 方法服务
1.java 并发编程的三个概念
原子性: 一个或多个 要执行过程中不会被其他因素打断,要么就都不执行
可见性: 多线程当中 一个线程改变某个值 其他线程都可以看到
有序性: 为了充分利用CPU 代码可能会不按照顺序执行 防止指令重排,按照代码的顺序执行
(1) voliate 可见性 指令重排 A 修改了值 B 能知道. 让B 不要在都本地内存
ACC_VOLIATE (JVM)->CPU(汇编指令)
使用场景:状态标记
(2)(hanppes-before 原则 )
(3)JVM中的内存屏障 LoadLoadBarrier LoadStoreBarrier StoreLoadBarrier StoreStoreBarrier
2.hanppens-before原则(JVM规定重排序必须遵守的规则)
•程序次序规则:同一个线程内,按照代码出现的顺序,前面的代码先行于后面的代码,准确的说是控制流顺序,因为要考虑到分支和循环结构。
•管程锁定规则:一个unlock操作先行发生于后面(时间上)对同一个锁的lock操作。
•volatile变量规则:对一个volatile变量的写操作先行发生于后面(时间上)对这个变量的读操作。
•线程启动规则:Thread的start( )方法先行发生于这个线程的每一个操作。
•线程终止规则:线程的所有操作都先行于此线程的终止检测。可以通过Thread.join( )方法结束、Thread.isAlive( )的返回值等手段检测线程的终止。
•线程中断规则:对线程interrupt( )方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupt( )方法检测线程是否中断
•对象终结规则:一个对象的初始化完成先行于发生它的finalize()方法的开始。
•传递性:如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C
3 .内存同步 volalite
用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作
就是 线程1,2同时从主内存取值到工作内存中,线程2修改值之后同步到主内存中,那么会强制1 从主内存从新读取.

四 锁
1.Synchronized monitotenter \monitotenterexit 底层jvm 监视器 很多参数 (让线程等待)
概念 重量级锁 和重入锁(反复上锁) jvm 锁
使用
Synchronized
(1) 方法(ACC_Synchronized)
Synchronized m(){ //锁对象
}
(2)代码块
Synchronized (A.class){
}
(3) j静态方法(锁类)
Synchronized m(){
}
两者区别
a. 对于普通同步方法 锁时当时实例对象
b.静态同步方法 锁时当前类对象
c. 静态代码块 锁 括号里面的
场景
竞争资源
2.lock 轻量级锁 手动锁 手动释放
ReentrantLock 轻量级锁,重入锁(可以反复上锁) 他可以分为公平锁和非公平锁 默认非公平的
(多看看里面方法) lock 和tryLock 可以等待一些时间再去获取,获取不到就算了(乐观锁)
try{
lock()
}catch(Exception e){
}finally{
//这里需要加入isHeldByCurrentThread 判断当前线程数量 需不需要释放锁,如果都没加锁 没必要释放
if(*.isHeldByCurrentThread()){
unlock
}
}
ReentrantLock 底层有个Condition (条件 )
Condition 生产 和消费 ,生产满了 就去消费 ,消费块没了 就去生产
3.cas :
⾸先,每个线程都会先获取当前的值,接着⾛⼀个原⼦的 CAS 操作,原⼦的意思就是这个 CAS 操作⼀定是⾃⼰完整执⾏完的,不会被别⼈打断。 然后 CAS 操作⾥,会⽐较⼀下说,唉!⼤兄弟!现在你的值是不是刚才我获取到的那个值啊? 如果是的话,bingo!说明没⼈改过这个值,那你给我设置成累加 1 之后的⼀个值好了! 同理,如果有⼈在执⾏ CAS 的时候,发现⾃⼰之前获取的值跟当前的值不⼀样,会导致 CAS 失 败,失败之后,进⼊⼀个⽆限循环,再次获取值,接着执⾏ CAS 操作!
CAS ABA 问题
Thread 1,Thread2,Thread 3 原值是10 Thread 1 改为20 Thread2 改为10 那么Thread3 读取到值时10 那么Thread3 会认为值没被更改
解决办法 加 标记
Atomic 体系
AtomicInteger:是对int 类型一个封装,提供原子性和更新操作
AtomicBoolean 等同于voalite
场景: 可以用于程序启动
场景 :
并发统计
原理:
cas 全称 compare And Swap 比较替换 其核心算法思想如下
函数 CAS(V,E,N) 参数:V 表示跟新的变量 E 预期值 N 新值
4.StampedLock 和 ReentranReadWriteLock 悲观锁 读写锁
写的时候上锁独享 不给其他人用,读到时候可以多个线程共享
5.AbstractQueuedSynchronizer 队列同步器 锁的底层 核心 核心 核心
底层实现原理
链表 和队列
6.CountDownLatch
允许一个或者多个线程等待其他线程完成操作
场景
并行计算
依赖启动
7.semaphore 信号量 和rentrantlock 方法很像
用来控制同时访问特定资源的 线程数量,他通过协调各个线程 保证合理使用公共资源
场景
限流和秒杀
限流
- redis+lua 分布式
- semaphore (JVM)
如果分布式 每台服务器 性能不一样,能接受的并发量不一样,那我们可以利用jvm 用 semaphore控制并发量
**8.condition 条件/状态 可以await()可以有多个等待室而object的wait 只有一个和signal()
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。
Condition是个接口,基本的方法就是await()和signal()方法;
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。
await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态
awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
signal() :唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
signal()All :唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
总结
javap -i **.class 字节码生成
jni java native intenrface java和c 之间的调用
voalite:防止指令重排,并单纯的读,写可以代替简单的锁,但是不保证原子性
cas :保证原子性,是整个并发包的基础
aqs 定义铜鼓队列等待队列,并发包的基础,其他的锁很多都是基于aqs拓展实现的
公平锁 和非公平锁(NofairSync)
一个线程过来公平锁先排序(进入等待队列),非公平锁表示 我先要抢抢试试,成功了先执行,不成功就去排队