• 进程与线程


    进程

      * 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须要将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘,网络等设备。进程就是用来加载指令,管理内存,管理IO的。 

      * 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。

      * 进程可以视为程序的一个实例。大部分程序可以运行多个实例(例如:记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如:网易云音乐、360安全卫士)

    线程

      * 一个进程之内可以有一到多个线程。

      * 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行。

      * Java中,线程是最小的调度单位,进程是资源分配的最小单位。在Windows中进程是不活动的,只是作为线程的容器。

    二者对比

      * 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集。

      * 进程拥有共享的资源,如内存空间等,供其内部的线程共享。

      * 进程间通信较为复杂

        * 同一台计算机的进程通信称为IPC(Inter-process communication)

        * 不同台计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如HTTP

      * 线程通信相对简单,因为他们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量。

      * 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低。

    线程运行原理

      我们都知道JVM中由堆、栈、方法区组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。

      * 每个栈由多个栈帧组成,对应这每次方法调用时所占用的内存。

      * 每个线程只能有一个活动栈帧,对应这当前正在执行的方法。

    当把这个TestFrames类加载进方法区后。 

    类加载完成后,这时候Java虚拟机就会启动一个名称叫做main的主线程,并给这个主线程分配一块线程栈内存。

     程序计数器:就是表示当前执行哪一行的代码。

    上下文切换

    因为一些原因导致cpu不在执行当前的线程,转而执行另一个线程的代码。

      * 线程的CPU时间片用完。

      * 垃圾回收

      * 有更高优先级的线程需要运行。

      * 线程自己调用了sleep,yield,wait,park,synchronized,lock等方法。

    当Context Switch发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器(Program Connter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的。

    状态包括程序计数器,虚拟机栈中每个栈帧的信息,如局部变量,操作数栈,返回地址等。

    Context Switch(上下文切换)频繁发生会影响性能。

    线程中常用方法

    sleep 

      * 调用sleep会让当前线程从Running状态进入Time Waiting状态(阻塞)。而且sleep不会释放锁。

      * 其他线程可以使用interrupt方法打断正在睡眠的线程。这时sleep方法会抛出一个InterruptedException异常。

      * 睡眠结束后的线程未必会立刻得到执行。(结束后会进入就绪状态)

      * 建议使用TimeUnit的sleep代替Thread的sleep来获得更好的可读性。

    yield

      * 调用yield会让当前线程从Running进入Runable(就绪状态),然后调度执行其他线程。(该线程仍然还有机会被调度)

      * 具体的实现依赖于操作系统的任务调度器。

    线程优先级

      * 线程优先级会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。

      * 如果CPU比较忙,那么优先级高的线程会获得更多的时间片。

    join

    用于同步,在另外一个线程中等待一个线程执行结束

     interrupt

    打断sleep,wait,join的线程。

    打断阻塞状态

    打断sleep的线程,会清空打断状态。即打断状态为:false

     打断正常运行的线程

    在主线程中打断t1线程。但是t1是并为真的打断。所以通过Thread.currentThread()获取到当前线程。isInterrupted()这里打断正在运行的线程结果就是true。

    interrupt打断park()

    LockSupport.park()方法,会让代码暂停不会向下执行。

     interrupt就可以打断,让它继续向下运行。

    但是打断标记为true后,park()就没有效果了 .park是根据打断标记来判断是否向下执行。

    Thread.interrupted()会查看是否打断,并把打断标记变为false

    park()与wait的区别

     两阶段终止模式

    流程图

    使用volatile实现

     

     线程的状态

    从操作系统层面(五种状态)

      * 初始状态:仅在语言层面创建了线程对象,还未与操作系统线程关联。

      * 可运行状态:(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行。

      * 运行状态:指已经获取了cpu

          * 当cpu时间片用完,会从运行状态转换至可运行状态,导致线程的上下文切换。

      * 阻塞状态:

         * 如果调用了阻塞API,如BIO读写文件,这时线程实际不会用到CPU,导致线程的上下文切换,进入阻塞状态。

         * 等BIO操作完成,会由操作系统唤醒阻塞的线程,转换至可运行状态。

         * 与可运行状态的区别是,对阻塞状态的线程来说,只要他们一直不唤醒,调度器就一直不考虑调度他们。

      * 终止状态:表示线程已经执行完毕,生命周期已经结束,不会转换为其他状态。         

    从Java API层面(根据Thread.State枚举,分为6种状态)

    NEW:

    RUNNABLE:调用了start() 

     TERMINATED:主线程有个等待sleep,所以这里的状态是TERMINATED

     TIMED_WAITING:有时间等待

     WAITING:没有时间的等待

     BLOCKED:想要获得锁,但是获得不到锁。

  • 相关阅读:
    基于Levenberg-Marquardt算法的声源定位matlab仿真
    网页文本编辑器
    Vue3的响应式更新是由什么实现的
    数据结构类型
    线程池相关总结
    【整理】HTTP相关版本对比
    matlab abel变换图片处理
    谈谈 Redis 数据类型底层的数据结构?
    GCC编译器
    【NowCoder】水题-[编程题]猫狗队列
  • 原文地址:https://blog.csdn.net/weixin_54401017/article/details/126802629