JavaEE(进程和线程)

操作系统

操作系统是一组做计算机资源管理的软件的统称。目前常见的操作系统有:Windows系列、Unix系列、Linux系列、OSX系列、Android系列、iOS系列、鸿蒙等。

进程

每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。这种假象是通过抽象了一个进程的概念来完成的,进程可以说是计算机科学中最重要和最成功的概念之一。
进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。

跑起来的程序才叫进程

CPU 分配 —— 进程调度

并行:微观上同一时刻,两个核心上的进程,就是同时进行的
并行:微观上,同一时刻,一个核心只能运行一个进程。但是能够对进程快速切换。

进程状态

就绪状态
运行状态
阻塞状态

内存分配 —— 内存管理

进程的虚拟地址空间:解决进程之间相互影响的问题。引入虚拟地址空间,地址越界能及时发现。

进程间通信

如上所述,进程是操作系统进行资源分配的最小单位,这意味着各个进程互相之间是无法感受到对方存在的,这就是操作系统抽象出进程这一概念的初衷,这样便带来了进程之间互相具备”隔离性
(Isolation)“。
但现代的应用,要完成一个复杂的业务需求,往往无法通过一个进程独立完成,总是需要进程和进程进行配合地达到应用的目的,如此,进程之间就需要有进行“信息交换“的需求。进程间通信的需求就应运而生。
目前,主流操作系统提供的进程通信机制有如下:

  1. 管道
  2. 共享内存
  3. 文件
  4. 网络
  5. 信号量
  6. 信号

其中,网络是一种相对特殊的 IPC 机制,它除了支持同主机两个进程间通信,还支持同一网络内部非同一主机上的进程间进行通信。

线程

“轻量级进程”。解决并发编程问题的前提下,让创建,销毁,调度的速度更快一些。和进程相比,剩下了申请资源、释放资源的操作。
一个线程就是一个 "执行流” 。 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 “同时” 执行着多份代码.

多进程也能实现 并发编程, 但是线程比进程更轻量.

  1. 创建线程比创建进程更快.
  2. 销毁线程比销毁进程更快.
  3. 调度线程比调度进程更快.

线程相关

  • 同一个进程里的多个线程之间,共用了进程的同一份资源(内存和文件描述符表)。内存是指,线程1new的对象,在线程2,3,4里都可以直接使用。文件描述符表指的是,线程1打开的文件,在线程2,3,4里都可以直接使用。
  • 操作系统在调度的时候,是以线程为单位进行的。线程是操作系统调度执行的基本单位,每个线程也都有自己的执行逻辑(执行流)。
  • 一个进程里可以有多个线程,一个线程不能存在多个进程里(可以进程间通信)
  • 增加线程的数量,也不一定能提高速度。CPU核心数量有限(每个核心操作一个线程)
  • 会有线程安全问题(线程不安全)。进程不会出现问题。

线程模型天然就是资源共享的,所以多线程容易争抢同一个资源
进程模型天然就是资源隔离的。进程间通信,访问同一个资源时,可能处理问题。

  • 如果一个线程抛异常,可能使整个进程杀掉。

创建一个线程

下图创建了一个简单的线程:
创建一个线程
这个代码通过t.start(),主线程调用了t.start,创建了一个新的线程。新的线程调用了t.run。当run执行完毕,线程自动销毁。

并发编程

下图中创建了一个线程,在线程里循环打印“thread”,然后再main方法里循环打印“main”,运行程序会得到什么:
一个多线程代码
可以看到,控制台里main和thread都出现了。这也就是程序在“并行”。
打印结果
从打印结果来看,“main”和“thread”并不是一前一后出现的。这源于操作系统调度线程时的“抢占式执行”,线程的具体先后顺序不确定,取决于操作系统调度器的具体实现策略。而从代码的角度来看,线程调度顺序好像是“随机”的一样。

start和run之间的区别:
start是真正创建了一个线程(从系统中创建的),线程是独立的执行流。
run只是描述了线程要做的事情。如果在main中直接调用run,那么不会创建线程。

Java中创建线程的写法

  1. 继承Thread类,重写run方法。
  2. 实现Runnable接口
    实现Runnable接口

让线程和线程的任务分离,解耦合

  1. 使用匿名内部类,继承Thread
    使用匿名内部类

  2. 使用匿名内部类,实现Runnable接口
    实现Runnable

这个写法和2本质相同

  1. 使用Lambd表达式
    lambda

推荐写法,简单~

Thread方法
Thread方法
Thread(Runnable target, String name)

Thread的常见属性
Thread属性

前台线程会阻止进程结束,后台线程(守护线程)不会。代码里手动创建的线程和main里的线程都是默认前台线程。

后台进程
这个代码中t.setDaemon(true); t.start();的顺序不能更换。
因为当一个线程已经启动后,不能再将其设置为守护(后台)线程。
非法状态

中断一个程序

中断(终止)的意思是,不是让线程立即就停止,而是通知线程应该停止。是否线程真正停止,取决于线程的具体代码写法。
中断线程

interrupt方法会触发sleep内部的异常,让sleep提前返回(被唤醒)

但是,这个程序在执行的时候,结果似乎和想象的不太一样:
执行结果
此时我们发现,线程在被打断之后,又继续执行了,这是怎么回事?

原因是这样的,此时程序里的interrupt会做两件事:

  1. 把线程内部的标志位(boolean)设置成true
  2. 如果线程处于sleep状态,则会唤醒sleep

但是sleep在被唤醒的时候,会将刚才设置成true的标志位,再重新设置回false(清空标志位)。 因此,这就导致当sleep的异常被catch完之后,循环仍然还要执行!!!!!
所以,这也就是前面所说的为什么中断一个程序,只是通知这个程序该被中断。

线程对中断请求的回应

  1. 忽略中断请求
    忽略中断请求
  2. 立即相应请求
    立即相应请求
  3. 稍后相应请求
    稍后相应请求
    中断相关的方法

等待一个线程 join()

等待一个线程

假设再让main阻塞之前,t线程就已经结束,那么join()不会阻塞main方法。也就是说,join是否进行阻塞也是看情况的

阻塞一些方法

获取当前线程的引用

获取线程的引用

休眠当前线程

休眠当前线程

线程的状态

线程的状态总共有六种

  1. NEW 表示创建了Thread对象,但还是没有调用start(内核里还没创建对应的PCB)
  2. TERMINATED 表示内核中的pcb已经执行完毕了,但是Thread对象还在

一个线程只能start一次 (规定)

  1. RUNNABLE 表示可运行的 (不是RUNNING)

RUNNABLE表示两种状态:
a. 正在CPU上执行的
b. 在就绪队列里,随时可以去CPU上执行

  1. WAITING
  2. TIMED_WAITING
  3. BLOCKED

以上三种都表示不同原因的阻塞(线程PCB正在阻塞队列中)

线程状态转换

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值