JavaEE 初阶(3)—— 多线程1之理解线程

   之前提到过,通过写特殊的代码,把多个CPU核心都能利用起来,这样的代码就叫“并发编程”。多进程编程,就是一种典型的并发编程。虽然多进程能够解决问题,但是随着对于效率要求越来越高,就希望有更好的并发编程。

  多进程编程,最大的问题 是 进程太“重”,创建进程/销毁进程 的开销比较大(时间、空间) 一旦需求场景 需要频繁的创建销毁进程,开销就非常明显了。最典型的-->服务器开发,针对每个发送请求的客户端,都创建一个单独的进程,由这个进程负责给客户端提供服务。

   为了解决 进程开销比较大 的问题,发明了“线程”。

  线程Thread:可以理解为 更轻量的进程,也能解决 并发编程  问题,但是创建/销毁的开销比较小。同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

  多线程的编程,成为当下主流的并发编程方式。多线程 基本是未来工作中天天都会用到的东西。

  所谓进程,在系统中 是通过 PCB 这样的结构体来描述(Linux),通过链表的形式组织起来......对于系统中,线程 同样也是通过 PCB 来描述的(Linux) 一个进程,其实是一组PCB;一个线程,是一个PCB  --> 存在了一个“包含关系” :一个进程可以包含 一个或多个 线程,此时每个线程 都可以独立到 CPU 上调度执行。

进程 是 系统“资源分配” 的 基本单位。
线程 是 系统“调度执行” 的 基本单位。

  一个可执行的程序,运行的时候(双击),操作系统就会创建进程,并给这个程序分配各种系统资源(CPU,内存,硬盘,网络宽带...)  同时也会在这个进程中创建出 一个或者多个 线程,这些线程再去CPU上“调度执行”。如果有多个线程在一个进程中,每个线程 都会有自己的 状态 优先级 上下文 记账信息,每个都会各自独立的在CPU上调度执行

   进程 包含 一个或多个 线程,不可以没有线程!!同一个进程中的这些线程,共用同一份系统资源的。

  之前说的“进程调度”是只包含一个线程的进程来说的,实际上 “进程调度” --> “线程调度”


   线程比进程 更轻量 --> 主要就在于 “创建线程”省去了“分配资源”过程,”销毁线程“也省去了“释放资源”的过程。

   一旦创建进程,同时也会创建第一个线程 --> 就会负责分配资源.....一旦后续创建第二个第三个线程...就不必重新再分配资源了(“分配资源”实际上是一个开销很大的事情...)

更加形象的解释人在房间里吃100只鸡的故事.......

“创建房间” 相当于 “资源分配”;“人” 相当于 “线程”;“人吃鸡” 相当于 “进程”(也叫“任务”)

JavaEE 初阶(3)—— 多线程1之理解线程_第1张图片JavaEE 初阶(3)—— 多线程1之理解线程_第2张图片


如果100只鸡交给一个人吃的话效率非常低....

JavaEE 初阶(3)—— 多线程1之理解线程_第3张图片


为了提高吃鸡的效率,可以引用多进程方案...

  如果 100只鸡分到两个房间里,每个房间50只鸡,两个人就可以在两个房间中“并发”完成,两个人比一个人吃的效率更快。

但是 为了创建一个进程,又分配了 房间、桌子 这样的资源....

JavaEE 初阶(3)—— 多线程1之理解线程_第4张图片JavaEE 初阶(3)—— 多线程1之理解线程_第5张图片


因此,为了减少资源分配,可以引用多线程方案....

房间、桌子都是同一个(没有再次引入系统资源),两个人一起吃效率也可以提高,与多进程版本是不遑多让,但是系统开销更小了。

JavaEE 初阶(3)—— 多线程1之理解线程_第6张图片


进一步增加线程的数目呢?

每个人只要负责 25  只即可,效率进一步提升....

JavaEE 初阶(3)—— 多线程1之理解线程_第7张图片

随着线程数目的增加,每个线程 负责完成的工作量 就少了。这些线程一旦同时开始工作,总的消耗时间 势必又会进一步减少...


再 进一步增加线程的数目呢?

如果再进一步增加线程数目,效率也没法进一步提升了....

桌子的空间是有限的,当人的数量达到一定程度,有些人就吃不到鸡了.....

JavaEE 初阶(3)—— 多线程1之理解线程_第8张图片

   能够提高效率 关键是 充分利用多核心 进行"并行执行";如果只是"微观并发”,速度没有提升...真正能提升速度的是“并行执行”。如果线程数目太多,比如超出了 CPU 核心数目,此时就无法再从微观上完成所有的“并行执行”,势必会存在严重的“竞争”。

   升级CPU --> 增加核心数 是有用的。即使多了更多的进程,cpu还是这些核心,此时多个进程里的线程之间,还是竞争同样数目的cpu 核心资源...

  由于多个线程  使用的是同一份资源 (内存资源 --> 代码中定义的变量/对象),当线程数目多了之后,此时就容易发生"冲突”:如果多个线程针对同一个变量进行读写操作(尤其是写操作),就容易发生冲突!!

   一旦发生冲突,程序就可能出现问题 --> “线程安全问题”(重难点内容 !!!)

   一个进程中 有多个线程 的时候,当某个线程 抛出异常,要及时妥善处理;一旦处理不当,就可能导致 整个进程都崩溃,因此其他线程就会随之崩溃了....


回顾:

并行"和“并发”从微观角度看 --> 多个核心 每个核心都可以 执行一个线程

这些 核心之间 的执行过程是 "同时执行" 的  --> 并行    一个核心 也可以按照 "分时复用"  来 切换多个线程

微观上,多个线程 是 "一个接一个"的执行的     由于调度速度足够快宏观上  看起来就好像 "同时执行" 一样 --> 并发


**进程线程的概念和区别**(必备知识!!!)

  1. 进程包含线程,进程可以有 一个或多个 线程,不可以没有线程
  2. 进程 是 系统“资源分配” 的 基本单位。
    线程 是 系统“调度执行” 的 基本单位。
  3. 同一个进程里的线程之间,共用同一份资源(内存,硬盘,网络带宽等....)
    尤其是“内存资源”,就是代码中定义的变量/对象....(因此 编程中,
    多个线程可以共用同一份变量
  4. 线程是当下实现“并发编程”的主流方式,通过多线程,就可以充分利用好 多核CPU。
    但是也不是线程数目越多越好,当线程数目达到一定程度,把多个核心都充分利用之后,此时继续增加线程,就无法再提高效率,甚至可能会影响效率(线程调度 也是有开销的)
  5. 多个线程之间,可能会相互影响 --> “线程安全问题
  6. 多个进程之间,一般不会相互影响:一个进程崩溃,不会影响到其他进程(这一点也称为“进程的隔离性”)

你可能感兴趣的:(程序员常识,java-ee,java)