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

引言

  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();
相关推荐
simple_ssn11 分钟前
【蓝桥杯】压缩字符串
java·算法
monstercl14 分钟前
【C#】元组
开发语言·c#
舒克日记26 分钟前
Java:189 基于SSM框架的在线电影评价系统
java·开发语言
Jelena技术达人28 分钟前
深入探索:获取翻译文本与语言词法分析的API接口
开发语言·爬虫
2401_8576100331 分钟前
中文学习系统:成本效益分析与系统优化
java·数据库·学习·架构
nbsaas-boot31 分钟前
如何更高效地使用乐观锁提升系统性能
java·服务器·数据库
转转技术团队31 分钟前
【述职黑话】ToB交易业务解决方案之状态机
java·状态模式
darkdragonking31 分钟前
解决POM依赖与maven仓库关联的问题
java·maven
m0_6724496041 分钟前
前后端分离(前端删除数据库数据)
java·数据库·mysql
飞的肖41 分钟前
在 Java 项目中集成和使用 dl4j 实现通过扫描图片识别快递单信息
java·ai·图像识别·dl4j