常见编程语言的功能,常见编程模型

  

  多线程编程模型线程安全术语串行、并发、并行串行:一个人,逐个完成任务。并发:一个人,战略性地同时做多件事。并行:很多人,各自做一件事。   

  

  共享竞态变量竞态的条件。   

  

  read-modify-write check-then-act线程安全如果一个类可以在多线程环境下正常运行,而不需要做任何修改,那么就叫线程安全。   

  

  线程安全问题的原子性   

  

  除了执行线程之外,任何线程都可以看到访问(读、写)共享变量的操作。此操作的要么已经执行结束要么是尚未发生,也就是说,其他线程不会“看到”此操作的中间效果。原子性只有在多线程环境中才有意义。如何实现原子性?   

  

  使用锁来利用CAS指令的Java语言中的原子操作。   

  

  所有变量的读操作都是原子的,除了long和double以外的任何类型变量(基类型、引用类型)的写操作都是原子性的可见点。   

  

  可见性是指一个线程对一个共享变量的更新结果对读取相应共享变量的线程是否可见。多线程程序的可见性问题意味着某些线程会读取到旧的数据,它会导致意想不到的后果。问题的原因   

  

  对内存的访问不是直接的。为了加快访问速度,相关操作会先在缓存中执行;此外,每个处理器都有自己的寄存器,这也可能导致不同的线程看到不一致的数据。   

  

  处理器通过寄存器、缓存、存储缓冲区和无效队列来读写内存,而不是直接处理主内存。   

  

  什么是性重新排序概念重新排序?   

  

  编译器可以改变两个操作的顺序,而不是按照程序的目标代码指定的顺序在一个处理器上执行多个操作。从其他处理器的角度来看,该序列可能与目标代码指定的序列不一致。重新排序的机遇和挑战   

  

  重新排序是对内存访问相关操作的优化,可以在不影响单线程程序正确性的情况下提高单线程程序的性能。但是,它可能对多线程程序的正确性产生影响。   

  

  重新排序的来源   

  

  编译器(如JIT编译器)几个与处理器和存储子系统(包括写缓冲区、存储缓冲区和缓存)相关的术语   

  

  顺序执行的源代码序列意识序列在此基础上,重新排序可分为如下:   

  

     

  

  指令重排序回顾:Java平台包括两个编译器:静态编译器(JavaC)和动态编译器(JIT编译器)。前者用于编译java源代码(。Java文本文件)转换成字节码(。类二进制文件),这涉及到代码编译阶段。后者的作用是将字节码动态编译成Java虚拟机主机的本地代码(机器码),参与Java程序的运行过程。   

  

  在Java平台中,静态编译器基本不执行指令重排序,而JIT编译器则可能执行指令重排序.   

  

  解释编译器如何优化代码:(从《Java多线程编程实战》)   

  

  重新排序处理器指令也被称为无序执行。   

  

  为了提高指令执行效率,现代处理器往往不是按照程序顺序逐一执行指令,而是按照动态调整指令的顺序执行,从而实现处理器乱序执行的哪条指令就绪就先执行哪条指令,在乱序处理器中,指令由处理器按照程序顺序一条一条地读取(即“顺序读取”),然后这些指令中哪一条准备好了就先执行,而不是完全按照程序顺序执行(即“乱序执行”)。   

  

  这些指令执行的结果(写寄存器或内存的操作)会先存储在重排序缓冲器(Rob)中,而不是直接写入寄存器或主存。重新排序缓冲器按照处理器读取相应指令的顺序将每个指令的执行结果提交(即,写入)到寄存器或者内存(即,“顺序提交”)。   

  

  在乱序执行的情况下,虽然指令的执行顺序可能不完全符合程序顺序,但处理器的指令重排序不会影响单线程程序的正确性,因为指令执行结果的提交(即反映在寄存器和内存中)仍然是符合程序顺序的。   

  

  猜测执行   

  

  比如处理器可以先执行IF语句的内容,并存储在ROB中,然后判断IF是否有效。如果有效,可以直接使用,如果无效,则丢弃。   

  

  当然,在多线程环境中,这也可能会导致行。   

程安全问题。

  

存储子系统重排序存储子系统

  

写缓冲器:对主内存的操作都是通过写缓冲器进行的高速缓存:处理器通过高速缓存访问主内存内存重排序

  

即使在处理器严格依照程序顺序执行两个内存访问操作的情况下,在存储子系统的 作用下其他处理器对这两个操作的感知顺序仍然可能与程序顺序不一致,即这两个操作的执 行顺序看起来像是发生了变化。这种现象就是存储子系统重排序, 也被称为 内存重排序 (Memory Ordering)。

  

内存重排序的类型

  

如果把读内存称为 Load,写内存称为 Store,则内存重排序有如下四种可能:

  

LoadLoad重排序StoreStore重排序LoadStore重排序StoreLoad重排序内存重排序与具体的处理器微架构有关,不同微架构的处理器允许的内存重排序也是不同的

  

貌似串行语义这个概念类似于 MySQL 中的可串行化和分布式中的 XX 概念

  

重排序也是遵循一定的规则的,我们要做到一种假象: 貌似串行语义 。也就是 从单线程程序的角度保证重排序后的结果不影响程序的正确性 。(但是不保证多线程环境下的正确性)

  

规则如下:

  

存在 数据依赖 关系的语句不会被重排序,只有不存在数据依赖关系的语句才会被重排序。存在 控制依赖 关系的语句可以允许被重排序,如之前的 猜测执行 。保证有序性在多线程角度下,从逻辑上(看上去)禁止重排序,从而保证有序性。

  

Java 的 volatile 关键字、 sychronized 等都能够实现有序性。

  

多线程模型的其他问题上下文切换

  

线程的活性故障这些由资源稀缺性或者程序自身的问题和缺陷导致线程一直处于非 RUNNABLE 状态,或者线程虽然处于 RUNNABLE 状态但是其要执行的任务却一直无法进展的现象就被称为 线程活性故障 :

  

死锁(哲学家进餐问题)锁死(没有唤醒线程,比如唤醒线程也睡眠了)活锁(一个线程对值做add,另一个做sub,导致程序一直进行,无法停止)饥饿(某些线程无法获得其所需资源,而使得任务无法进展)资源争用与调度概念

  

一次只能被一个线程占用的资源被称为 排他性资源资源被一个线程访问时,其他线程试图访问该资源的现象被称为 资源争用 。我们要达到的理想状态是: 高并发、低争用资源调度的公平性

  

资源调度的一个常见特性是:他是否保证 公平性 (是否先到先得)。

  

非公平调度策略是我们多数情况下的首选资源调度策略,其优点是 吞吐量大 ,缺点是资源申请者申请资源所需时间的是 偏差可能较大 ,并可能导致 饥饿 现象。

  

公平调度适合在 资源的持有线程占用资源的时间相对长资源的平均申请时间间隔相对长的情况下 ,或 对申请的时间偏差有要求的情况下 使用,优点和缺点则反之。

相关文章