【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
云原生网关 MSE Higress,422元/月
简介: 几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。

1. 进程和线程

1.1 进程

几乎所有的操作系统都支持进程的概念,所有运行中的任务通常对应一个进程(Process)。

  • 当一个程序进入内存运行时,即变成一个进程。

进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。

  • 独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
  • 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。
  • 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

1.2 线程

大部分操作系统都支持多进程并发运行,现代的操作系统几乎都支持同时运行多个任务。
多线程则扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。

线程(Thread)也被称作轻量级进程(Lightweight Process),线程是进程的执行单元

  • 就像进程在操作系统中的地位一样,线程在程序中是独立的、并发的执行流
    当进程被初始化后,主线程就被创建了。

对于绝大多数的应用程序来说,通常仅要求有一个主线程,但也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程,每个线程也是互相独立的。
线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。
线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。
因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,因为需要确保线程不会妨碍同一进程里的其他线程线程可以完成一定的任务,可以与其他线程共享父进程中的共享变量及部分环境,相互之间协同来完成进程所要完成的任务。

简单来说:

  • 进程:进程是系统进行资源分配和调度的基本单位,可以将进程理解为一个正在执行的程序。
  • 线程:线程是程序执行的==最小单位==,一个进程可由一个或多个线程组成。
    总结:操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程。

1.3 Java中实现多线程的方式

Java语言提主要供了两种实现线程的方式:

  • 继承Thread类创建线程类
  • 实现Runnable接口创建线程类

1.3.1 继承Thread类创建线程类

实现步骤:

  • ①定义类继承Thread类,并重写Thread类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行体。
  • ②创建Thread子类的实例,即创建了线程对象。
  • ③调用线程对象的start()方法来启动该线程。

1.3.2 实现Runnable接口创建线程类

实现步骤:

  1. 定义类实现Runnable接口,并重写Runnable接口的run()方法,该run()方法的方法体就代表了线程需要完成的任务。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调用该Thread对象的start()方法来启动该线程。

1.4 Java线程状态

线程的五种状态:

  • 1)新建状态(New):线程对象实例化后就进入了新建状态。
  • 2)就绪状态(Runnable):线程对象实例化后,其他线程调用了该对象的start()方法,虚拟机便会启动该线程,处于就绪状态的线程随时可能被调度执行。
  • 3)运行状态(Running):线程获得了时间片,开始执行。只能从就绪状态进入运行状态。
  • 4)阻塞状态(Blocked):线程因为某个原因暂停执行,并让出CPU的使用权后便进入了阻塞状态。
    • 等待阻塞:调用运行线程的wait()方法,虚拟机会把该线程放入等待池。
    • 同步阻塞:运行线程获取对象的同步锁时,该锁已被其他线程获得,虚拟机会把该线程放入锁定池。
    • 其他线程:调用运行线程的sleep()方法或join()方法,或线程发出I/O请求时,进入阻塞状态。
  • 5)结束状态(Dead):线程正常执行完或异常退出时,进入了结束状态。

1.5 Thread类常用基本方法

返类型 构造器/方法 说明
Thread() 分配新的 Thread 对象。
Thread(Runnable target) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)。
static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
long getId() 返回该线程的标识符。
String getName() 返回该线程的名称。
int getPriority() 返回线程的优先级。
boolean isAlive() 测试线程是否处于活动状态。
void join() 等待该线程终止。
void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
void setName(String name) 改变线程名称,使之与参数 name 相同。
void setPriority(int newPriority) 更改线程的优先级。参数范围[1,10]

1.6 线程安全问题

许多在单线程情况下的代码放到多线程环境下容易出现线程安全问题。

1.6.1 同步代码块

为了解决线程安全问题,Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块。

同步代码块的语法格式如下:

synchronized (obj) {
   
    //同步代码块
}

obj叫做同步监视器(锁对象),任何线程进入下面同步代码块之前必须先获得对obj的锁;其他线程无法获得锁,也就执行同步代码块。

  • 这种做法符合:“加锁-修改-释放锁”的逻辑。锁对象可以是任意对象,但必须保证是同一对象

任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后该线程会释放对该同步监视器的锁定。

1.6.2 同步方法

除了同步代码块,Java的多线程安全支持还提供了同步方法

  • 同步方法也是使用synchronized关键字来修饰某个方法,则该方法称为同步方法。

对于synchronized修饰的实例方法(非static方法),无须显式指定同步监视器,同步方法的同步监视器就是this,也就是调用该方法的对象。

1.6.3 同步锁(Lock)

从Java5开始,Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当
Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性。

Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

在实现线程安全的控制中,比较常用的是 ReentrantLock(可重入锁)。使用该Lock对象可以显式地加锁、释放锁。

2. 线程安全

2.1 死锁(资源分配)

当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁出现。

一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。

在系统中出现多个同步监视器的情况下很容易发生死锁。

2.2 wait()、notify/notifyAll()

  1. wait()、notify/notifyAll()方法是Object的final方法,无法被重写。
  2. wait()使当前线程阻塞,前提是必须先获得锁,一般配合synchronized关键字使用,即,一般在synchronized同步代码块里使用wait()、notify/notifyAll()方法。
  3. 由于wait()、notify/notifyAll()在synchronized代码块执行,说明当前线程一定是获取了锁的。
    当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
    只有当notify/notifyAll()被执行时候才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait(),再次释放锁。也就是说,notify/notifyAll()的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll()后立即退出临界区,以唤醒其他线程。
  4. wait()需要被try...catch包围。
  5. notify和wait的顺序不能错,如果A线程先执行notify方法,B线程在执行wait方法,那么B线程是无法被唤醒的。
  6. notify和notifyAll的区别
  • notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。
  • notifyAll会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll方法。

3. 网络编程的基础知识

3.1 计算机网络

计算机网络是指两台或更多的计算机组成的网络,在同一个网络中,任意两台计算机都可以直接通信,因为所有计算机都需要遵循同一种网络协议。

  • 局域网LAN(LocalAreaNetwork):一般指覆盖范围在10公里以内,一座楼房或一个单位内部的网络。由于传输距离直接影响传输速度,因此,局域网内的通信,由于传输于距离短,传输的速率一般都比较高。目前,局域网的传输速率一般可达到10MB/S和100MB/S,高速局域网传输速率可达到1000MB/S。

  • 广域网WAN(WideAreaNetwork):是指远距离的、大范围的计算机网络。跨地区、跨城市、跨国家的网络都是广域网。由于广域的覆盖范围广,联网的计算机多,因此广域网上的信息量非常大,共享的信息资源很丰富。
    INTERNET是全球最大的广域网,它覆盖的范围遍布全世界。

  • 城域网MAN(MetropolitanAreaNetwork):其覆盖范围在局域网和广域网之间。一般指覆盖范围为一个城市的网

TCP/IP协议泛指互联网协议,其中最重要的两个协议是TCP协议IP协议
只有使用TCP/IP协议的计算机才能够联入互联网,使用其他网络协议(例如NetBIOS、AppleTalk协议等)是无法联入互联网的。

3.2 MAC地址、IP地址

MAC地址(物理地址),用于表示网卡的唯一编号。
一般是在网卡出厂时由厂家写入硬件的唯一地址。

在网络中,一个IP地址用于唯一标识一个网络接口(Network Interface)。
一台联入互联网的计算机肯定有一个IP地址,但也可能有多个IP地址。

IP地址分为IPv4IPv6两种。
IPv4采用32位地址,类似192.168.1.11。

  • IPv4地址总共有2^32个(大约42亿),目前已经基本用尽。

IPv6采用128位地址,类似2001:0DA8:100A:0000:0000:1020:F2F3:1428。

  • IPv6地址则总共有2^128个。IPv6的地址是根本用不完的,可以宣传可以为地球上的每一粒沙子分配一个IP。

特殊IP地址:127.0.0.1 本机地址

如果一台计算机只有一个网卡,并且接入了网络,那么,它有一个本机地址127.0.0.1,还有一个IP地址,例如192.168.1.11,可以通过这个IP地址接入局域网络。

如果一台计算机有两块网卡,那么除了本机地址,它可以有两个IP地址,可以==分别接入两个网络。比如服务器==

4. 序列化和反序列化

return Json() --> json字符串--> ajax把Json字串串转为Json对象
请求 ajax发送Json对象 -->json字符串--> Controller.action(类 实例)进行了反序列化

  • 目的:==把对象进行网络传输或存储==

对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。

  • 其他程序一旦获得了这种二进制流(无论是从磁盘中获取的,还是通过网络获取的),都可以将这种二进制流恢复成原来的Java对象。

4.1 序列化的含义和意义:

序列化机制允许将实现序列化的Java对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象。

序列化机制使得对象可以脱离程序的运行而独立存在。
对象的序列化(Serialize)指将一个Java对象写入IO流中,

与此对应的是,==对象的反序列化(Deserialize)== 则指从IO流中恢复该Java对象。
如果需要让某个对象支持序列化机制,则必须让它的类是可序列化的(serializable)。

为了让某个类是可序列化的,该类必须实现如下两个接口之一:

  • Serializable(常用)
  • Externalizable

Java中很多类已经实现了Serializable,该接口是一个标记接口,实现该接口无须实现任何方法,它只是表明该类的实例是可序列化的。
所有可能在网络上传输的对象的类都应该是可序列化的,否则程序将会出现异常。

4.1 Properties类

  • Properties 类是Java中操作配置文件的类

5. Optional 类

Java应用中最常见的bug就是空值异常。

在Java 8之前,Google Guava引入了 Optionals 类来解决 NullPointerException,从而避免源码被各种null检查污染,以便开发者写出更加整洁的代码。

Java 8 将Optional加入了官方库。
Optional仅仅是一个容易存放T类型的值或者null的类。它提供了一些有用的接口来避免显式的null检查,可以参考Java 8官方文档了解更多细节。

修饰符 返回值类型 方法 说明
public static Optional of(T value) 创建一个容器,不能为空,否则报空指针异常
public static Optional ofNullable(T value) 创建一个容器,可以包含null
public T get() 取容器里面的值,如果为空会抛出 NoSuchElementException
public boolean isPresent() 判断容器中是否存在对象
public void ifPresent(Consumer<? super T> consumer) 如果容器中存在对象,则执行 consumer
public Optional filter(Predicate<? super T> predicate) 筛选
public Optional map(Function<? super T, ? extends U> mapper) 取容器对象中的属性
public T orElse(T other) 如果对象存在,则输出,对象不存在,则返回 other
public T orElseGet(Supplier<? extends T> other) 如果对象存在,则输出,对象不存在,则返回 other
public T orElseThrow(Supplier<? extends X> exceptionSupplier) 若是对象不存在,则抛出异常

6. ❤️Java基础专栏 - 前篇回顾

7. 💕👉 其他好文推荐

目录
相关文章
|
27天前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
95 1
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
存储 Java 编译器
说一说关于序列化/反序列化中的细节问题
我是小假 期待与你的下一次相遇 ~
|
5月前
|
JSON Java 数据库连接
|
6月前
|
存储 安全 IDE
说一说序列化与反序列化中存在的问题
本文详细解析了Java中的序列化机制,包括序列化的概念、实现方式及应用场景。通过Student类的实例演示了对象的序列化与反序列化过程,并分析了`Serializable`接口的作用以及`serialVersionUID`的重要意义。此外,文章还探讨了如何通过自定义`readObject()`方法增强序列化的安全性,以及解决可序列化单例模式中可能产生的多实例问题。最后提供了代码示例和运行结果,帮助读者深入理解序列化的原理与实践技巧。
144 4
|
6月前
|
JSON JavaScript 前端开发
Go语言JSON 序列化与反序列化 -《Go语言实战指南》
本文介绍了 Go 语言中使用 `encoding/json` 包实现 JSON 与数据结构之间的转换。内容涵盖序列化(`Marshal`)和反序列化(`Unmarshal`),包括基本示例、结构体字段标签的使用、控制字段行为的标签(如 `omitempty` 和 `-`)、处理 `map` 和切片、嵌套结构体序列化、反序列化未知结构(使用 `map[string]interface{}`)以及 JSON 数组的解析。最后通过表格总结了序列化与反序列化的方法及类型要求,帮助开发者快速掌握 JSON 数据处理技巧。
|
12月前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
420 1
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第3天】在Java编程的世界里,对象序列化与反序列化是实现数据持久化和网络传输的关键技术。本文将深入探讨Java序列化的原理、应用场景以及如何通过代码示例实现对象的序列化与反序列化过程。从基础概念到实践操作,我们将一步步揭示这一技术的魅力所在。