什么是程序?
程序=指令+进程
进程:程序一次执行的过程 一个程序可以多次执行(多个进程)甚至可以“同时”执行(宏观下)
从OS来讲:进程就是OS进行资源分配的基本单位 也是最小实体
同一个进程中的资源是共享的 不同进程之间的资源是隔离的
进程的区分:
新建(进程正在创建中),就绪(等待CPU),运行(进程的指令在CPU运行着)
阻塞(进程由于等待外部条件 暂时无法执行)
结束(进程的所有指令执行结束蛋PCB暂时保留 OS还需要做一些其他工作)
操作系统(OS)的五个模块
进程管理(重要)
内存管理 文件管理(IO操作)
网络管理
驱动管理
OS会有一套机制 让进程之间进行必要的数据交换——进程间的通信
进程间通信的常见方式:
1管道
2消息列队
3信号量
4信号
5共享内存
6网络 ——用得较多
内存管理中主要研究的问题:
1管理那些内存已经被分配,那些内存暂时没有被分配
2分配出去的内存何时和如何回收
3物理内存——线性地址转换
4内存碎片
内存管理:以空间为单位的
【内核使用的内存】【分配给普通进程使用的内存】【空闲内存】 空间划分不保证是连续的
JVM的内存空间分为栈区 堆区 方法区 JVM是OS眼中的普通进程 OS分配给JVM进程的内存
线性地址(虚拟地址) 和物理地址
物理地址:真实的内存中的地址
线性地址:物理地址被OS进行转换后的一个地址
在研究OS实现时,可能你面临的一些问题
1 分配资源时 如何避免死锁(dead lock)的问题
线程(thread)
1OS系统层面上的线程
Thread(线)
2一个线程一定属于一个进程 一个进程可以有多个线程(至少有一个线程 )
通常一开始就存在的线程称为主线程
主线程和其他线程之间地位是相等的
3进程的概念是资源隔离的 所以进程之间数据通信注定是一个高成本工作
现实中一个任务需要多个执行流一起配合完成 是非常常见的
为方便数据通信的执行流 线程就成为了这一角色
4什么是线程
线程是OS进行调度的基本单位(调度:CPU分配)
线程变成了独立执行流的承载概念 进程退化成只是资源(不含CPU)的承载概念
进程:OS资源的分配单位(不包括cpu资源)
线程:执行流承载单位 OS进行调度的基本单位
线程之间的执行是相互独立的
程序的一次执行过程为一个进程 main所在的线程为主线程 主线程中可以运行
对应的操作来创建运行其他线程
OS针对同一个进程下的线程实现“连坐”机制:一旦一个线程异常退出OS会关闭该线程所在的整个进程
线程的创建和销毁不涉及资源分配问题 所以回收成本低
java线程 VS OS 线程
java有JVM 使得java多进程开发很少 Java线程一个关闭 不会连坐
java的线程还克服了很多os线程的缺点
在代码中创建线程:
1继承Thread类 重写run方法
实例化该类对象-》Thread对象
import java.util.concurrent.TimeUnit;
public class AboutThread {
static class SomeThread extends Thread {
@Override
public void run() {
int i = 0;
while (true) {
System.out.println("我是另一个线程(执行流 B): " + (i++)); // 语句1
try {
TimeUnit.MILLISECONDS.sleep(139); // 语句2
} catch (InterruptedException e) {
}
}
}
}
public static void main(String[] args) {
// 在主线程中,利用 SomeClass 对象,创建一个新的线程出来
SomeThread st = new SomeThread();
st.start();
int i = 0;
while (true) {
System.out.println("我是主线程(执行流 A): " + (i++)); // 语句1
try {
TimeUnit.MILLISECONDS.sleep(257); // 语句2
} catch (InterruptedException e) {
}
}
}
}
2 通过实现runnable(让线程去执行的任务)接口 重写run方法
Runnable接口
Runnable接口
创建线程的另一种方法是声明实现 Runnable 接口的类。该类实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
实现Runnable接口的意义:Runnable接口用来指定每个线程要执行的任务。包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。
创建线程的步骤。
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
//实现Runnable接口public class MyRunnable implements Runnable {
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args) {
//创建线程任务对象
MyRunnable my=new MyRunnable();
//创建线程对象
Thread t=new Thread(my);
Thread t2=new Thread(my);
//开启线程
t.start();
t2.start();
}
3启动线程
对于java程序,我们需要通过Thread对象(银行卡)来控制线程的一切
而JVM(Hotspot)内部维护着关于线程的其他数据(一般商业银行)
OS内部:实现java线程的OS线程的相关数据
我们拥有的只是这张银行卡 我们对银行卡的操作真正核心的数据在后面封装着
手中有一个Thread 对象时调用其start()方法开启线程
线程状态:新建——就绪——运行——结束
Start()的作用:新建——就绪:不负责分配CPU 线程被加入到线程调度器的就绪队列中等待被调度器选中分配CPU 从子线程就绪的那一刻起子线程和主线程就完全平等了
理论上主线谁先谁后都有可能 但大部分先是主线程 因为start()在主线程上 执行到这里发生线程调度的概率不大
什么时候会发生线程调度:
1CPU空闲:
当前运行的CPU执行结束 运行——结束
等待外部条件 运行——阻塞
主动放弃 运行——就绪
2被调度器主动调度
高优先级栈现场抢先
时间片耗尽(较常见)