hymn

忽有故人心头过,回首山河已是秋。

  menu
132 文章
0 浏览
7 当前访客
ღゝ◡╹)ノ❤️

随笔(思考)

上线文切换

内核态 和 用户态 的本质就是权限的问题。
内核态可以“为所欲为”,用户态则受到了极大的限制,只有访问内存的权限。

多线程涉及到上线问切换,因为应用线程只有访问内存的权限,没有拥有cpu执行时间片的。

sync设计到线程的阻塞,阻塞本质就是退出cpu的执行时间片,所以要从用户态切换到内核态。
内核可以调用系统资源,cpu,硬件,网络,io,权限大。

redis之所以设计成单线程的,是因为redis的数据都在内存中,不涉及硬盘的io,或者说不需要用到内核,
所以设计成单线程,如果设计成多线程,就会设计到线程的上下文切换,开销增加了,
所以设计成单线程发挥他的最大作用。

ThreadLocal 和 InheritableThreadLocal

主线程创建线程 new InheritableThreadLocal,会把数据放到inheritableThreadLocals 中,
当主线程创建线程子线程时 new Thread() 的init  方法会查看主线程的inheritableThreadLocals是否为空,
不为空时会把主线程中的值inheritableThreadLocals,通过init复制一份到子线程

如果想让子线程访问父线程的ThreadLocal,就
父线程创建 InheritableThreadLocal,
创建的子线程会复制父线程的inheritableThreadLocals 到子线程的中,达到子线程访问父线程值的效果。

Java sync

本质sync锁的是一个对象,当多个线程去竞争一个对象时,只有一个线程会获得锁,
获得的这个锁会在这个对象的对象头的锁标记标记为锁定(1),其他的锁看到这个对象的锁标记为1时,会阻塞,
这些线程会从用户态转到内核态,来调用阻塞操作,因为阻塞操作是内核态的操作,用户态没有权限,会发生上下文切换。这
些锁会阻塞,阻塞的意思就是退出cpu的时间片(只有内核态才有权限去操作cpu的调度,这也是会发生上下文切换的原因),
java中会把这个阻塞的线程放到一个线程中,等待系统的调用。等这个拥有锁的线程执行完毕后或者这个线程中断,
会退出这个锁,系统会去调用这个队列中的一个线程去执行,去争抢式的去获取锁。

内存可见性

voletile
读取:直接从主存读取数据
修改:修改的值直接刷新回主存

sync
线程1修改的值会在线程2中可见,在线程1修改的时候线程2阻塞,当线程2进入时,
线程2内的工作内存失效,直接从主存拿取数据来操作。

volatile

jvm允许编译器和处理器指令重排,以提高运行性能。
比如如果两个操作没有依赖性,则有可能会发生指令重排。
int a = 1;
int b = 1;
int c = a + b;
前两句可能会发生指令重排,调换顺粗。

内存可见性 : 线程修改的内容会立刻刷新到主存

防止指令重排 :  可以理解为一个“栅栏”,volatile之前的指令不会被重排到volatile后面,之后的指令不会放到前面。
int a = 1;
boolean ready = true;
这两句操作之间没有关联性,可能会发生指令重排,多线程下可能会发生错误。
在ready上假声volatile 会防止指令重排,就是int a = 1;的操作一定会发生在 boolean ready  = true;之前

不是原子性 :(i++)

程序运行的局部性原理 和 伪共享问题

cpu加载数据时,当在高级缓存中没有找到时,会去主存中获取,获取的数据不是单单一条数据,
会把地址连续的数据一起记载到高级缓存。
但是当多个cpu去操作相同的缓存行时,根据缓存一致性协议,当前cpu中的高级缓存中的数据会失效,
会去二级缓存中获取或者去主存中获取,这样就会性能地下,java中解决的办法是字节填充,
通过将一个数据的大小填充到只有一个缓存行的大小,当cpu去加载数据时就会只加载一个数据,
而不会加载多条数据。单线程则会充分利用局部性原理,同时加载多条数据,来提高性能。

标题:随笔(思考)
作者:hymn
地址:https://dxyhymn.com/articles/2020/12/21/1608544746722.html