干货-并发编程提高——线程的实现(四)

引言

  1. 计算机中,中央处理器的运算是最快的,但是大多数的运算都不能只依靠中央处理器独自完成。处理器至少需要与内存的交互。这种I/O操作是很难消除的。所以现代的计算机中都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存。来做为处理器与内存之间的缓冲。
  2. 高速缓存很好的解决了处理器与内存的速度矛盾。但是也带来了新的问题,缓存一致性问题。所以产生了缓存一致性协议。
  3. 为了使得处理器内部的运算单元尽可能的充分被利用。处理器可能会对输入的代码进行乱序执行优化。处理器会在计算机之后将乱序执行的结果重组。保证该结果与顺序执行的结果一样。如果存在一个计算任务依赖于另外一个计算任务的中间结果。那么其顺序性就不能靠代码的先后顺序来保证了。
  4. Java中的JIT编译器也有类似的指令重排序优化。

计算机中线程的实现:

  1. 使用内核线程实现<Kernel-Level Thread - KLT>,直接由操作系统内核支持的线程。这种线程由内核来完成切换。内核通过操作调度器对线程进行调度。并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身。程序一般不会直接去使用内核线程。而是去使用内核线程的一种高级接口。轻量级进程<Light Weight Process - LWP>。轻量级线程与内核线程之间是1:1的关系。由于内核线程的支持,每个轻量级进程都成为一个独立的调度单元。即使有一个轻量级的进程在系统调度中阻塞了。也不会影响整个进程继续工作。由于是基于内核线程的实现。所以各种线程操作,如创建,析构及同步都需要进行系统调用。而系统调用的代价相对较高。需要在用户态(User Mode)和内核态(Kernel Mode)中来回切换。每个轻量级进程都需要有一个内核线程的支持。因此轻量级进程要消耗一定的内核资源。因此一个系统支持轻量级进程的数量是有限的。
  2. 使用用户线程实现,只要不是内核线程,就可以认为是用户线程。是不是有点儿像类加载器?用户线程的建立,同步,销毁调度完全在用户态中完成。不需要内核的帮助。如果程序实现得当。这种线程不需要切换到内核态,因此操作是可以非常快速且低消耗的完成的。也可以支持更大规模的线程数。使用用户线程的优势是不需要系统内核支援。劣势也是在没有系统内核的支援。所有的线程操作,都需要用户程序自己处理。线程的创建,切换,调度都是需要考虑的问题。
  3. 用户线程加轻量级进程的混合实现,即存在于用户线程又存在于轻量级进程。用户线程可以完全建立在用户空间中,因此用户线程的创建,切换,析构都操作依赖廉价。并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁。这样可以使用内核提供的线程调度功能及处理器映射。并且用户线程的系统调用要通过轻量级线程来完成。极大的降低了整个进程调度功能被完全阻塞的风险。
  4. Java线程的实现是基于称为"绿色线程"的用户线程实现的。一条java线程映射到一条轻量级进程之中。而在Solaris平台中,操作系统线程可以同时支持一对一,及一对多的线程模型。
  5. 线程切换,阻塞的过程是很耗费CPU资源的。举例:AQS添加链表。
  6. Yeild方法,让出CPU的执行时间。依旧回退到就绪状态(Runable)。能让不能得。就比如有个人骑着一辆自行车,调用Yeild方法后就从自行车上下来了。此时此刻有很多人盯着这辆自行车。但是此人也可能手脚麻利 又再次获得此自行车。所以并不可靠。
  7. Join方法底层是使用wait方法实现的,所以调用join方法,会释放锁。Join方法形同骑着一辆自行车,然后找到指定的人,跟他说,你骑吧,我睡儿会。哪个线程执行的x线程join方法,哪个线程阻塞。等到x线程执行完才会被唤醒。Join方法并不需要在同步块儿中执行。
  8. wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写,相应的sleep方法是可以被重写的,如果你手贱的话。
  9. wait()方法使当前线程处等待状态,wait方法必须先持有锁。其实也就对应了wait方法必须在同步块儿中执行,当然在同步块儿中了,也就获得了锁。
  10. 如果线程处于New状态,并未启动,此时调用它的join方法是不起作用的。程序将继续向下执行。因此时被调用的线程还未准备好(非就绪状态Runable)。
  11. 可以使用线程的join方法保证执行顺序。相当于一个递归操作。
    1. Thread05->Thread04.join->Thread03.join->Thread02.join->Thread01.join
    2. 实则执行的顺序是01->02->03->04->05
  12. 我们调用wait方法时是必须要在同步体里的,然而join方法本身就是由synchronized所修饰的。参看下方:所以调用join方法时就已经获取到了t1对象的锁。在主线程中调用了t1的join方法,代表主线程获取到了t1对象的锁,参看join方法的源码得知。此时内部的wait方法便是将主线程置为等待状态。
    1. 参看:Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); t1.join();
相关推荐
plmm烟酒僧1 分钟前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
测试界的酸菜鱼13 分钟前
Python 大数据展示屏实例
大数据·开发语言·python
让学习成为一种生活方式16 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画22 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend30 分钟前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
希言JY1 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
残月只会敲键盘1 小时前
php代码审计--常见函数整理
开发语言·php
xianwu5431 小时前
反向代理模块
linux·开发语言·网络·git
Heavydrink1 小时前
HTTP动词与状态码
java