Java基础 -- 线程、程序、进程的基本概念和联系,线程的基本状态,final关键字,Java 中的异常处理

发布于:2023-01-09 ⋅ 阅读:(272) ⋅ 点赞:(0)

1. 线程、程序、进程的基本概念和联系


1.1 程序

程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。


1.2 进程

进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。

程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列

进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。
在这里插入图片描述

进程是程序的⼀次执⾏过程,因此进程是动态的。系统运⾏⼀个程序即是⼀个进程从创建,运⾏到消亡的过程。

在 Java 中,当我们启动 main 函数时其实就是启动了⼀个 JVM 的进程,⽽ main 函数所在的线程就是这个进程中的⼀个线程,也称主线程。

如下图所示,在 windows 中通过查看任务管理器的⽅式,我们就可以清楚看到 window 当前运⾏的进程(.exe ⽂件的运⾏)。
在这里插入图片描述


1.3 线程

线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位。

一个进程可以由很多个线程组成,线程间共享进程的所有资源(堆和⽅法区资源)。每个线程有自己的有⾃⼰的程序计数器、虚拟机栈和本地⽅法栈。所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作时,负担要⽐进程⼩得多,也正因为如此,线程也被称为轻量级进程

线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

在这里插入图片描述

Java 程序天⽣就是多线程程序,我们可以通过 JMX 来看⼀下⼀个普通的 Java 程序有哪些线程,代码如下。

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class MultiThread {
    public static void main(String[] args) {
        // 获取 Java 线程管理 MXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        // 不需要获取同步的 monitor 和 synchronizer 信息,仅获取线程和线程堆栈信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,
                false);
        // 遍历线程信息,仅打印线程 ID 和线程名称信息
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() + "] " +
                    threadInfo.getThreadName());
        }
    }
}

运行结果:

在这里插入图片描述


1.4 小节

进程是资源分配的基本单位,它是程序执行时的一个实例,在程序运行时创建;线程是程序执行的最小单位,是进程的一个执行流,一个进程由多个线程组成的。


2. 线程的基本状态

Java 线程在运⾏的⽣命周期中的指定时刻只可能处于下⾯ 6 种不同状态的其中⼀个状态(图源《Java并发编程艺术》4.1.4 节)。
在这里插入图片描述
线程在⽣命周期中并不是固定处于某⼀个状态⽽是随着代码的执⾏在不同状态之间切换。Java 线程状态变迁如下图所示(图源《Java 并发编程艺术》4.1.4 节):
在这里插入图片描述
由上图可以看出:

线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。

操作系统隐藏 Java 虚拟机(JVM)中的 READY 和 RUNNING 状态,它只能看到 RUNNABLE 状态(图源:HowToDoInJava:Java Thread Life Cycle and Thread States),所以 Java 系统⼀般将这两个状态统称为 RUNNABLE(运行中) 状态 。
在这里插入图片描述
当线程执行 wait() 方法之后,线程进入 WAITING(等待)状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnablerun()方法之后将会进入到 TERMINATED(终止) 状态。


3. final关键字


3.1 final修饰类

用final修饰的类不能被继承,没有子类。

  • 例如:我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经被String类定义为final的了。
    在这里插入图片描述

  • 我们也可以定义final修饰的类:
    在这里插入图片描述


3.2 final修饰方法

用final修饰的方法可以被继承,但是不能被子类的重写。

  • 例如:每个类都是Object类的子类,继承了Object中的众多方法,在子类中可以重写toString方法、equals方法等,但是不能重写getClass方法 wait方法等,因为这些方法都是使用fianl修饰的。
    在这里插入图片描述

  • 我们也可以定义final修饰的方法:
    在这里插入图片描述


3.3 final修饰变量

  • 用final修饰的变量表示常量,只能被赋一次值,其实使用final修饰的变量也就成了常量了,因为值不会再变了(其实是内存地址不会变)
    在这里插入图片描述在这里插入图片描述

3.4 小结

final关键字主要用在三个地方:变量、方法、类。

  1. 对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。(其实都是内存地址不变)
  2. 当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。
  3. 使⽤ final ⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;第⼆个原因是效率。在早期的 Java 实现版本中,会将 final ⽅法转为内嵌调⽤。但是如果⽅法过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升(现在的 Java 版本已经不需要使⽤ final⽅法进⾏这些优化了)。类中所有的 private ⽅法都隐式地指定为 final。

4. Java 中的异常处理


4.1 Java 异常类层次结构图

在这里插入图片描述

在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。


4.2 Error(错误)

Error(错误)是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。


4.3 Exception(异常)

Exception(异常)是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeExceptionRuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。

注意:异常能被程序本身处理,错误是无法处理。


4.4 Throwable类常用方法

  • public string getMessage():返回异常发生时的简要描述。
  • public string toString():返回异常发生时的详细信息。
  • public string getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同。
  • public void printStackTrace():在控制台上打印Throwable对象封装的异常信息。

4.5 异常处理总结

  • try 块: 用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
  • catch 块: 用于处理try捕获到的异常。
  • finally 块: 无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。

注意:

当try语句和finally语句中都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally语句的返回值将会覆盖原始的返回值。

在这里插入图片描述

在这里插入图片描述

可以发现,最终都是返回的final代码块的值



本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到