并发编程(二)—synchronized和volatile
synchronized保证读和写的安全,volatile保证读的安全,但不保证写的安全,在多处理器开发中保证了共享变量的“可见性”。
可见性:当一个线程 修改一个共享变量时,另外一个线程能读到这个修改的值。
一、高速缓存
1.1 高速缓存
如ALU在对数据进行for循环操作时,如果在计算完成后立刻返回更新数据,会出现一个运算核心在进行读写操作时一直占据总线的情况,此时CPU多核处理器就失去意义,所以在CPU内部设置高速缓存用于存储数据。ALU将数据存储在高速缓存中,此时就释放了总线,其他ALU就可以通过总线对其他数据进行操作。高速缓存的存储空间越大,CPU与总线的交互就越少,计算机的性能就越好。
- 为了避免电压信号的干扰,不同的指令不能同时传输,即指令在总线必须排队传输
- 内存同一时刻只能被一个指令指挥
1.2 多级缓存
在计算机的任务管理器中我们可以看到,计算机的CPU中设置了三级缓存,这是为什么?
多级缓存的目的也是为了加快传输速率,提高每个核心的利用率。
CPU从内存中读取数据,一个导线同一时刻只能传输一个电压信号,必须等该信号传输完成后才能继续传输下一个,传输的过程对于CPU是浪费的。
设置多级缓存后相当于设置了数据中转站,数据到达第一个中转站后下一个数据便可发出,以此进行传输,提高了内存的数据发送频率与CPU的数据接收频率,但是由于数据转发也需要消耗时间,目前平衡性能最好的是三级缓存。
二、synchronized和volatile
2.1 高速缓存带来的问题
通过对高速缓存的讲解,我们可以看到高速缓存支持多核CPU且提高CPU的利用率,但是它也带来了并发问题—相互覆盖。
如上图,若核心1对a进行10次的+1操作,计算完成后时间片到期,此时核心2对a进行计算,同样计算10次,随后返回更新,此时a=10,核心2任务完成后释放CPU,核心1继续执行,返回更新,会覆盖核心2返回的数据,即对a进行了20次操作,但其计算得到结果为10。
注:
- 高速缓存中的数据在返回更新时,会将高速缓存中的该数据清空,下次进行计算时,需要重新从内存中读取数据。
- 高速缓存存储容量满了之后会返回更新,即更新是随机的,不一定是任务执行完毕后才返回更新。
2.2 volatile(轻量级锁)
2.2.1 相关术语定义
术语 | 英文 | 描述 |
内存屏障 | memory barriers | 在内存中有一个变量标记,该标记为某一确定值时不要继续访问。 |
缓冲行 | cache line | 高速缓存的基本存储单元。 |
原子操作 | atomic operations | 一系列操作全部成功或者全部失败,如果一个操作失败,其他操作还原。 |
缓存命中 | 读取数据时会先查看高速缓存中是否存在该数据,有则直接读取(缓存命中),没有则到内存中读取。 |
缓冲行
- 计算机中具有存储能力的区域:内存(页)、硬盘(栈区)、CPU内部—高速缓存 。在计算机中最小的物理单元是1字节一个存储单元,操作系统给内存和硬盘划分的逻辑存储是4kB一个存储单元,高速缓存中的存储单元是缓冲行(64字节),每一个缓冲行和外界相连的导线进行数据交互。
- 每个缓冲行都有唯一的线路,一个缓冲行可以存储多个任务的数据,当一个任务对其进行操作时(不同芯片有所不同,可能加载全部数据,也可能只加载一部分数据),占据该缓冲行的线路,相当于占据整个缓冲行,其他任务不能进行操作。
缓冲行填充
操作系统指挥驱动,驱动调动硬件,为了指挥驱动,操作系统内核也是C语言。
计算机底层为C语言,各种复杂的类型都会转成C语言的基本类型(short、int、long、float、double、char),所以缓存行中存储的都是1字节到8字节的数据,若存储的数据多,读取数据时就需要排队找到想读取得数据,影响性能。若数据量较少,可以将缓存行全部用一个任务的数据填充,会大幅提升性能,但是数据量较多时,高速缓存中存储的数据过少就会影响性能。
因此,设计一个缓存行的大小为64字节,存储的数据量不会过大ÿ