操作系统基础

操作系统基础

操作系统

操作系统是一个搞管理的软件,是软件、硬件和用户之间交互的媒介

进程

进程是一个重要的软件资源,是由操作系统内核负责管理的。这里的管理包括描述和组织,
描述:讲清楚有哪些属性特征,使用结构体描述进程属性,用来描述进程的结构体有一个特殊的名字叫做PCB(进程控制块)

组织:通过一定的数据结构,把多个这样的基本单位串起来。通过双向链表把多个PCB串到一起。

PCB中描述了进程的特征有:

  1. pid------进程的身份标识符(唯一的数字)
  2. 内存指针------指向了自己的内存是那些
  3. 文件描述符表 ------硬盘上的文件等其他资源

进程调度的相关属性
1) 进程的状态
就绪状态:随叫随到,随时准备好去CPU上执行
运行状态:正在运行的进程
阻塞状态:短时间内无法到CPU上执行(比如进程在进行密集的IO操作,读写数据时)

2)优先级
3)上下文
操作系统在进行进程切换时,需要把进程执行的中间状态记录并保存好,以在下次这个进程再次运行的时候可以恢复上次的状态。
上下文的本质就是存档的内容。
进程的上下文,就是CPU中各个寄存器的值
保存上下文,就是把这些CPU寄存器的值保存到内存中
恢复上下文就是把内存中这些寄存器恢复到原来的值
4)记账信息
操作系统会统计每个进程在CPU上占用的时间和执行的指令数目,决定下一阶段如何进行调度。

进程的虚拟地址空间

程序中所获取到的内存地址,并不是真实的物理内存的地址,而是经过一层抽象,虚拟出的地址
虚拟地址空间的主要作用就是避免进程之间相互产生影响。引入虚拟地址空间后,如果A进程出翔了bug,就只有A崩溃,其他的进程不会受到影响。

进程间通信

但是引入进程隔离后。又出现了新的问题
有时候进程之间又需要进行数据之间的交互。
解决方法:
建立一个多个进程都可以访问到的公共空间,基于这个公共空间来进行交互数据

进程与线程

进程,最主要的目的是为我们解决并发编程这样的问题。
CPU进入了多核时代,为了进一步提高程序的执行速度,我们需要充分利用CPU的多核资源。

多进程已经可以用来解决并发编程的问题,可以利用CPU的多核资源了,那我们为什么还需要线程呢?
因为进程太“重”了—消耗的资源多并且速度慢。这里的重,主要就表现在资源回收与分配上
创建,销毁和调度进程,开销都比较大。

因此线程应运而生,线程又称为轻量级进程,线程在解决并发编程的前提下,还让创建、销毁】调度的速度更快。
线程轻的原因是因为他省了申请资源和释放资源的操作。

进程与线程之间的关系:进程包含线程
一个进程可以包含一个线程,也可以包含多个线程(不能没有线程)
进程中只要第一个线程启动时开销比较大,后面就省事了。
同一个进程的多个线程之间,共用了进程的同一份资源(这里的资源主要指的是内存和文件描述符表)【比如说在线程1中new的对象,在线程2,3,4中都可以直接使用】【在线程1中打开的文件,在线程2,3,4中都可以直接使用】

当一个进程中有多个线程时,每个线程都是独立在CPU上调度的,也就是线程是操作系统调度执行的基本单位。每个线程有自己的执行逻辑(执行流)。

一个线程也是通过一个PCB描述的,一个进程里可能对应一个PCB,也可能对应多个
每个线程的PCB都有各自记录自己的状态,上下文,优先级,记账信息
但是同一个进程中的PCB中,pid,内存指针、文件描述符都是一样的。

进程专门负责资源分配
线程用来接管和调度相关内容

但是线程数量也不是越多越好的,
线程数量过多,核心数量有限,开销反而会浪费在线程调度上。
多个线程争抢同一个资源(变量),有可能发送线程抢占问题(线程不安全)
如果一个线程抛出异常,没有处理好,很可能整个进程都会异常。

进程模型:是资源隔离的,不容易触法资源争抢。在进行进程间通信的时候,多个进程访问同一资源,可能会出问题。
线程模型:是资源共享的,多个线程容易去争抢同一个资源。

在Java中进行多线程编程
关于线程的操作,是靠操作系统提供的API,Java很多操作系统提供的功能,都被JVM封装了。
Java操作多线程的核心类:Thread

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}

public class demo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
    }
}

在这里创建了MyThread类,继承Thread类。重写了Thread类中的run()函数。

new Thread对象并不创建线程(系统内核里的PCB),调用start才是真正创建PCB,真正创建线程。
t.start()这里的工作就是创建了一个新的线程,新的线程负责去执行t.run()。

package thread;

class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello Thread");
            try {
                Thread.sleep(1000);   //休眠一秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class threadDemo {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();  //创建了一个新的线程,新的线程负责执行t.run()
//        t.run();   //这样就进不来下面,只是一个单线程
        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里就有两个线程 :新创建的线程Thread 和主线程main

在这里插入图片描述

操作系统在调度线程时,执行的是抢占式执行。
线程安全问题的由来就是 抢占式执行,随机调度。

start和run的区别:
start:真正的创建的一个线程(从系统这里创建),线程是独立的执行流
run:只是描述了线程所需要做什么。如果直接在main中调用run,此时没有创建新的线程,只有主线程main一个。

jdk自带的jconsole可以查看当前Java进程中的所有线程。

Java中有多种写法可以创建线程

  1. 继承Thread ,重写run
class MyThread extends Thread {
	@Override
	public void run() {
		System.out.println("Hello world");
	}
}

public class ThreadDemo1 {
	public static void main(String[] args) {
		Thread t = new MyThread();
		t.start();
	}
} 
  1. 实现Runnable接口
class MyRunnable implements Runnable {
	@Override
	public void run() {
		System.out.println("Hello world");
	}
}

public class ThreadDemo2 {
	public static void main(String[] args) {
		Runnable runnable = new MyRunnable();  //这里只是描述了一个任务
		Thread t = new Thread(runnable);   //把任务交给线程来执行
		t.start();
	}
}

这里吧线程和线程要干的活分开,耦合比较低,未来改代码时代码改动较小。

  1. 使用匿名内部类继承Thread
public class ThreadDemo3 {
	public static void main(String[] args) {
		Thread t = new Thread() {
			@Override
			public void run(0 {
				System.out.println("Hello world");
			}
		};
		t.start();
	}
}

创建了一个Thread的子类 ,子类没有名字【匿名】
创建了子类的实例,并且让 t 引用指向该实例

  1. 使用匿名内部类实现Runnable
public class ThreadDemo4 {
	public static void main(String[] args) {
		Thread t = new Thread( new Runnable() {
			@Override
			public void run(0 {
				System.out.println("Hello world");
			}
		});
	}
}

这里的写法和2本质相同,只是把实现Runnable的任务交给了匿名内部类来实现
在这里创建了一个类,实现了Runnable,同时创建了类的实例,并传给Thread的构造方法。

  1. 使用lambda表达式
public class ThreadDemo5 {
	public static void main(String[] args) {
		Thread t = new Thread(() -> {
			System.out.println("Hello world");
		});
		t.start();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值