多线程与并发详解第一版

越学越不会怎么办,头发秃了怎么办,老子曾说过:是人就得认命,小拇指是拗不过大腿的。 面试要求会的是越来越多,只能悄悄的打工学习。

在计算机中,线程是重中之重。在JAVA中,多线程也亦是如此。多线程写的好,代码执行效率更高 写这篇文章前,看了很多线程的文章,想着该如何写。(有人突然问我线程是什么,总是回答的模棱俩可,不够准确,下面从操作系统的角度开始一步一步知晓线程,学习线程)

线程的概念(线程由来)

多来自于操作系统教程一书。 在传统的操作系统中,进程是系统进行资源分配的基本单位,按进程为单位分给存放其映象所需要的虚地址空间、执行所需要的主存空间、完成任务需要的其他各类外围设备资源和文件。同时,进程也是处理器调度的基本单位,进程在任一时刻只有一个执行控制流,通常将这种结构的进程称单线程(结构)进程(single threaded process) 。

    进程的开销大,当频繁的调度线程会耗费大量的CPU时间,而且每个进程都需要分配存储空间,这样就限制了操作系统中的进程数量 进程之间的通信代价很大,每次通信都要涉及到通信进程与操作系统之间的信息传递 进程间的并发颗粒度较大,过多的进程切换和 通信延迟会使得细粒度并发得不偿失 对于有多个处理器和分布式计算环境来说,进程之间频繁的通信和切换会降低并行度 对于c/s结构来说,频繁需要输入输出并同时大量计算的服务器进程,例如:服务器数据库效率会下降

随着时间发展,单一执行控制流开始乏力。这就迫切要求操作系统改进进程结构,提供新的机制,使得应用能够按照需求在同一进程中设计出多条控制流,多控制流之间可以并行执行,多控制流切换不需通过进程调度;多控制流之间还可以通过内存区直接通信,降低通信开销。这就是近年来流行的多线程(结构)进程(multiple threaded process) 。如果说操作系统中引入进程的目的是为了使多个程序能并发执行,以改善资源使用率和提高系统效率,那么,在操作系统中再引入线程,则是为了减少程序并发执行时所付出的时空开销,使得并发粒度更细、并发性更好。这里解决问题的基本思路是:把进程的两项功能--“独立分配资源”与“被调度分派执行”分离开来,前一项任务仍由进程完成,它作为系统资源分配和保护的独立单位,不需要频繁地切换;后一项任务交给称作线程的实体来完成,它作为系统调度和分派的基本单位,会被频繁地调度和换,在这种指导思想下,产生了线程的概念。 传统的操作系统一般只支持单线程,但是目前很多著名的操作系统都支持了多线程,JAVA的运行引擎则是单进程多线程的例子。 以上大白话来说,就是早期传统的单一执行流程开始无法满足社会需求,线性执行的弊端开始出现。基于这个问题,出现了“独立分配资源”,“被调度分派执行”

多线程内存布局入下图所示,在多线程的环境中,进程任然有进程控制块和用户地址空间,而每个线程处理有独立堆栈,以及包含线程信息的系统堆栈外,还需要线程控制块。一个进程中,线程间的关系较为密切,因为这些线程是同享进程中拥有的资源,线程是驻留在相同的地址空间中,可以存取相同的数据,例如:当一个线程改变了主存中的一个数据时,如果这个时候其他线程也读取这个数据项,它可以看到其他线程改变后的数据结果(后面会引出voliate这个关键字的讲解)

多线程环境中,进程的定义:进程是操作系统中进行‘保护’和‘资源分配’的基本单位

    一个虚拟地址空间,用来容纳进程的映象 对处理器、和其他进程、文件和I/O资源等有控制和保护的访问 传统进程原先所承担的控制流执行任务交给了称作线程的部分来完成

多线程环境中线程的概念

线程是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。线程是进程的组成部分,每个进程内允许包含多个并发执行的实体(控制流),这就是多线程。同一个进程中的所有线程共享进程获得的主存空间和资源,但不拥有资源。线程具有:

  1. 线程执行状态(运行、就绪、等待、…);
  2. 当线程不运行时,有一个受保护的线程上下文,用于存储现场信息。所以,观察线程的一种方式是运行在进程内一个独立的程序计数器;
  3. 一个执行堆栈
  4. 一个容纳局部变量的主存存储区。

以上这么多,总结下来呢。进程是管理和分配资源的最小单位,而进程中线程是执行的最小单位。 更加直白干脆也能这么理解,线程就是一个执行任务中不同的执行路径。 就像是三峡大坝,开一个出水口也是出水,开一排出水口也是出水,不过出一排出水口的效率更高罢了。

线程的创建 现在的创建就不需要多讲了,诸君都懂,无非就是继承Thread类或者实现runnable接口,若还有什么创建的办法,那么可以用线程池创建线程。

线程的声明周期 要区分好,操作系统的线程与java中的线程。 这是Thread类中存在的6种状态

public enum State {
          
   
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

(1)新生状态(NEW):这个状态就是new了一个线程,但是还未调用start().方法启动。

(2)就绪状态(RUNNABLE): –ready :这个状态就很微妙了,已经调用了start方法,所有的线程都添加到了一个就绪队列中,进行抢占CPU资源 –running:当前进程获取到cpu资源后,此进程就绪队列中的线程就会去抢夺cpu资源,谁先抢到谁先执行,执行的过程就是运行状态。

(3)阻塞状态(BLOCKED):当一个线程试图获取一个对象锁,而该对象锁被其他线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。

(4)等待状态(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

(5)超时等待(TIMED_WAITING):处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

(6) 死亡状态 (TERMINATED):当线程执行完毕或者出现异常导致程序结束,就是死亡状态

经验分享 程序员 微信小程序 职场和发展