初识多线程

本篇不涉及任何编码层面知识 只谈理论 长期编辑


读Java多线程有感,读后发现,整个内容知识体系,并不是三言两语能够概括清楚的,如线程间通信、上下文切换、资源争抢、锁的互斥、重排序、内存屏障、三大特性:“可见行,原子性,有序性”等等。接下来一一说明博主的理解,不一定对,但是可以带着这些理论去我们的实践中产生怀疑。

无竞争资源情况


无论其他,一个执行单元而已,这就是线程。多线程,那就是多个执行单元。
线程资源独立,无资源竞争示意图
如上图,多个线程并行执行(在多核心cpu的情况下真正并行)。单纯看上图,这就是我们日常中的编程,单独在任何一个线程内,声明自己对象实例,同一时间内,只有单个线程在访问一个对象实例。这不存在线程安全问题,因为没有资源竞争!
初识多线程_第1张图片
业务分派子线程执行图

主线程处理完成部分业务,直接返回用户,用户不用过多等待,子线程继续执行可延迟的逻辑。没有资源竞争

抽象线程安全


初识多线程_第2张图片
多线程资源竞争模型

  • 线程间的通信

    如图:执行中的线程,通过总线把内存中的数据加载到自己的高速缓存中,进行计算,把结果返回给内存中。可见,线程间中通信是通过不断在内存中的读写操作实现的
    上文提到过,线程就只是一个执行单元而已,我们这里把它想象成一个售票员,而高速缓存就是售票员查票系统。内存我们理解为票的库存系统。假设还有1张票的时候,4个票务员同时查询,反馈给购票人还有余票一张,4个售票员同时销售则会发生超卖问题,这是因为线程间的操作没有被正确的通信到其他的竞争资源线程上。线程计算的值是错误的。
  • 竞争带来的问题

    如上图,多个线程并行执行购票处理,而ticket的余数将会是各个线程争相操作的公共资源,既然线程是执行单元,那么肯定受到CPU的执行调度影响,当server真正的多核心的时候,出现真正的并行(而非单核心时间片轮转算法)。由于CPU执行速度远在其他硬件之上,所以计算往往在CPU内部的高速缓存上去执行计算。然而,高速缓存毕竟是容量比较小的,这就决定必须要从其他存储介质上获取数据。而处理高速cache之外,离CPU最近的当属内存。一些公共的数据,都存在内存中,数据只有一份,多个核心,多个处理单元一起执行,与之带来的一个问题,就是高速缓存中数据不一定正确!!

  • 串行粒度

    既然高速缓存中的数据不一定正确,那么为了维持它的正确性,这使得所有线程并行处理,必须更改为串行处理,一个一个来。才能保证正确,可以这样讲:“存在资源竞争的情况下,为了保证线程安全,并行总有一点会成为串行,串行粒度越小性能越高,越大性能越低(锁的区域)

保证线程安全–锁


初识多线程_第3张图片
总线锁和内存锁

  • 锁的简介

    锁是一种保证线程安全的措施,它保证了同一时间对同一资源有竞争关系的线程只有一个能够在运行态,其他线程则在外面阻塞,也就是上文我们所说的并行变更串行的一种解决方案

  • 再剖串行粒度-总线锁(高粒度)

    如图,CPU执行链接内存,必经过总线。那么当一个线程读取,更改内存中共享资源时,把总线锁住,不让其他线程通信,不就保证了线程安全吗?简单粗暴。但是粒度较大,这样的话,其他线程就无法执行了,性能被降低很多

  • 再剖串行粒度-总线锁(低粒度)

如图,假设共享资源变量1.其他资源不存在竞争关系,锁住总线未免有些小题大做了,这就好像是房间里有个蚊子,把门窗全部关上直到打死?为了提高性能,单独锁住内存中有竞争关系的内存区块,缩小锁的粒度,不存在竞争关系的线程依然可以并行执行,这类似于ConcurrentHashMap中的Segment,先做Hash再锁局部区域。

三大特性

要保证线程安全,三大特性缺一不可,但是如果全部保证,我们即可说线程是安全的。
  • 原子性

    如果一个操作是原子操作,我们就称该操作是具备原子性,所谓原子操作,是说一个整体!不可分割的操作单位。Java中提供了13个原子操作类,值得一提的是,基本类型中long和double的赋值写入操作并非原子的
  • 可见性

    可见性值得是线程之间的操作可见。也就是说。当一个线程对公共资源做出更改,那么对其他线程而言。这个更改是立即可见的。在CPU执行层面,我们可以理解到:计算都是在高速缓存中计算的。当计算完毕,还未来得及写入到内存,其他线程拿到旧的值也进行了计算。那么我们称为该操作是内存不可见的。
  • 有序性
初识多线程_第4张图片
重排序类别

说到有序性,不的不提的就是重排序。重排序的种类如上图所示,一般来讲,为了优化我们的程序,编译器可能会对我们的程序进行重排序,以此来达到优化性能的体验。在Java中,重排序遵循单线程的依赖关系不进行重排序。但不存在依赖关系的程序很有可能被重排序。甚至一个锁内引用语句,部分指令会被重排序到锁外,构造函数内代码被重排序到构造函数外。这都是有可能的。所以引发了double-check等线程安全问题。这里指的一提的是happens-before关系和as-if语义,所以,在多线程执行环境中,能否按照编写者本意的顺序去执行,有序性的保障非常重要!
有序性是指我们程序的执行顺序要严格保证我们编码过程中的代码流程执行,禁止重排序。

上下文切换


时间片分配算法理论


你可能感兴趣的:(Java多线程,Java多线程)