操作系统--

简介: -

线程概念

程序:就是应用程序,是静态的,通俗来讲就是我们写的代码,

进程:动态的程序,是程序的一次运行,也可以是进行中的程序,有自身的产生,存在,消亡

线程:线程是进程的实体,一进程可以多线程,比如qq的多个聊天窗口可以同时聊天

并发:多个任务交替执行,单核cpu实现多任务就是并发

并行:多个任务同时执行,并行是真正意义上的同步实现,多核cpu

区别:

每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。

进程是独立的,但线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。

线程状态

/**

* @author 伍六七

* @date 2022/8/1 15:30

*/

public class ThreadState {

   public static void main(String[] args) throws InterruptedException {

       T t = new T();

       System.out.println(t.getName()+"状态"+t.getState());

       t.start();

       while (Thread.State.TERMINATED!=t.getState()){

           System.out.println(t.getName()+"状态"+t.getState());

           Thread.sleep(500);

       }

       System.out.println(t.getName()+"状态"+t.getState());

   }

}

class T extends Thread{

   @Override

   public void run() {

       for (int i = 0; i < 3; i++) {

           System.out.println("执行次数"+i);

           try {

               Thread.sleep(1000);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       }

   }

}


线程常用方法

线程优先级

每个线程都有优先级,优先级的高低只与线程获得执行机会的次数多少有关,并非是线程优先级越高的就一定先执行,因为哪个线程的先运行取决于CPU的调度,无法通过代码控制。

  • MAX_PRIORITY=10,最高优先级
  • MIN_PRIORITY=1,最低优先级
  • NORM_PRIORITY=5,默认优先级

相关方法

  • int getPriority() :返回线程的优先级
  • void setPriority(int newPriority) : 设置线程的优先级

主线程的默认优先级为5,如果线程A创建线程B,那么A与B是相同的优先级,但由于不同操作系统的设置不同,不建议自定义优先级

Thread方法

创建Thread方法

/**

* @author 伍六七

* @date 2022/8/1 13:56

*/

public class SellTicket {

   public static void main(String[] args) {

       //Thread

       SellTicket01 sellTicket01 = new SellTicket01();

       SellTicket01 sellTicket02 = new SellTicket01();

       SellTicket01 sellTicket03 = new SellTicket01();

       sellTicket01.start();

       sellTicket02.start();

       sellTicket03.start();

       

       //Runnable

       SellTicket02 sellTicket = new SellTicket02();

       new Thread(sellTicket).start();

       new Thread(sellTicket).start();

       new Thread(sellTicket).start();

       

       //lambda

       new Thread(() -> {

           System.out.println(1);

       },"sellTicket00").start();

   }

}


//使用Thread

class SellTicket01 extends Thread{

   @Override

   public void run() {

       System.out.println(1);

   }

}


//Runnable接口

class SellTicket02 implements Runnable{

   @Override

   public void run() {

       System.out.println(1);

   }

}

run()和start()

  • run():普通方法重写,会阻塞,执行完该方法才向下走。
  • start():启动线程->最终执行run方法。

调用run方法并没有开启新的线程,它仍旧是在主线程里

start()形成子线程原理

start0()是本地方法,JVM机调用

sleep() 方法和 wait()

先回顾一下线程休眠

线程休眠:让运行中的的线程暂停一段时间,进入计时等待状态

  • 调用sleep后,当前线程放弃CPU,进入计时等待状态,但仍然占用该cpu资源,在指定时间段之内,该线程不会获得执行的机会,此状态下的线程不会释放同步锁/同步监听器
  • wait是进入等待池等待,让出系统资源,其他线程可以占用cpu,一般wait不会加时间限制。

sleep方法和yield方法的区别:

  • 共同点是都能使当前处于运行状态的线程放弃CPU时间片,把运行的机会给其他线程;
  • 不同点在于:sleep方法会给其他线程运行机会,但是并不会在意其他线程的优先级;而yield方法只会给相同优先级或者更高优先级的线程运行的机会;
  • 调用sleep方法后,线程进入计时等待状态,而调用yield方法后,线程进入就绪状态

yeild() 方法和join()

  • yeild():当线程A的yield方法执行后,线程不会释放锁,而是释放了CPU的执行权,将当前执行的线程重新变为就绪状态,让CPU重新选择要执行的线程,也有可能当前线程执行完yield方法后,CPU又一次选中这个线程执行。该方法主要用于调试或者测试,比如在多线程竞争条件下,让错误重现现象或者更加明显。
  • join():当线程A调用线程B的join,那么系统会在此时将A置于等待状态先执行线程B,等到线程B执行完成后再开始继续执行线程A

用户线程与守护线程

用户线程与守护线程也被称为前台线程与后台线程

守护线程会伴随用户线程的结束而结束,其目的是为其他线程提供服务常见的:垃圾处理机制

设置线程为守护线程的方法,该方法必须在start方法之前调用,否则会触发IllegalThreadStateException异常,因为线程一旦启动,就无法对其做修改了。

  • .setDaemon(true);

/**

* @author 伍六七

* @date 2022/8/1 14:31

*/

public class ThreadMethod_join {

   public static void main(String[] args) throws InterruptedException {

       ThreadTest01 threadTest01 = new ThreadTest01();

       threadTest01.setDaemon(true);

       threadTest01.start();

       for (int i = 0; i < 5; i++) {

           Thread.sleep(50);

           System.out.println("主线程");

       }

   }

}

class ThreadTest01 extends Thread{

   @Override

   public void run() {

       int i = 10;

       while (i-->0) {

           try {

               Thread.sleep(50);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

           String name = Thread.currentThread().getName();

           System.out.println(name+"执行了");

           if(i==4){

               Thread.yield();

           }

       }

   }

}

如何预防和避免线程死锁?

售票员-资源抢夺冲突

package com.wuliuqi.ticket;


/**

* @author 伍六七

* @date 2022/8/1 13:56

*/

public class SellTicket {

   public static void main(String[] args) {

       //Thread

       SellTicket01 sellTicket01 = new SellTicket01();

       SellTicket01 sellTicket02 = new SellTicket01();

       SellTicket01 sellTicket03 = new SellTicket01();

       sellTicket01.start();

       sellTicket02.start();

       sellTicket03.start();

}

class SellTicket01 extends Thread{


   private static int ticketNum = 100;

   @Override

   public  void run() {

       while (ticketNum>0) {

           try {

               Thread.sleep(50);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

           System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

       }

   }

}

超卖问题-没票时return

if(ticketNum<=0){

   Loop=false;

   System.out.println("没票了");

   return;

}

synchronized解决资源冲突

注意用implements Runnable,extends Thread会重复

//用synchronized修饰方法

//不要修饰run方法,否则会一个线程买完所有票

public synchronized void Sell()

情况1-成功

修饰Sell方法,在run方法执行被修饰的Sell方法,解决了资源冲突

class SellTicket02 implements Runnable{

   private static int ticketNum = 100;

   boolean Loop = true;

   public synchronized void Sell(){

       if(ticketNum<=0){

           Loop=false;

           System.out.println("没票了");

           return;

       }

       System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

   }

   @Override

   public  void run() {

       while (Loop){

           Sell();

           try {

               Thread.sleep(10);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       }

   }

}

情况2-独享

修饰run方法,实现了票的冲突,但是被独享了

class SellTicket02 implements Runnable{


   private static int ticketNum = 100;

   boolean Loop = true;

   @Override

   public synchronized void run() {

       while (Loop){

           if(ticketNum<=0){

               Loop=false;

               System.out.println("没票了");

               return;

           }

           System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

           try {

               Thread.sleep(10);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       }

   }

}

synchronized修饰方法-

//修饰方法

//对象锁

private static int ticketNum = 100;

boolean Loop = true;

public synchronized void Sell(){//synchronized修饰方法

   if(ticketNum<=0){

       Loop=false;

       System.out.println("没票了");

       return;

   }

   System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

}

//用对象锁this

public void Sell(){

   synchronized (this){

       if(ticketNum<=0){

           Loop=false;

           System.out.println("没票了");

           return;

       }

       System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

   }

}

//用引用锁Object

private static int ticketNum = 100;

boolean Loop = true;

private static Object lock=new Object(); //必须是静态的。

public void Sell(){

   synchronized (lock){//synchronized(object)

       if(ticketNum<=0){

           Loop=false;

           System.out.println("没票了");

           return;

       }

       System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

   }

}

//用类锁class

public class SynchronizeMain {

}

class test{

   public void Sell(){

       synchronized (SynchronizeMain.class){//写类

           if(ticketNum<=0){

               Loop=false;

               System.out.println("没票了");

               return;

           }

           System.out.println("窗口"+Thread.currentThread().getName()+"抢到了票"+"剩余票"+(--ticketNum));

       }

   }

}

实例锁--引用锁(常用,多个对象同一把锁)--类锁


对象就是实例一个类可以有多个实例,每个实例都可以有自己的锁

同步局限性:执行效率降低

一个实例只能有一个锁对象,只能修饰方法和代码块。所以属于对象的,并且可以加锁的就是非静态方法和非静态代码块。

为什么要用 synchronized (object) 这种方式,去多创一个object对象

引用锁就是说在类中创建一个任意类型的引用,假如说你创建一个静态的,那么这个引用属于类本身,使用这个引用加锁,就相当于使用类加锁的效果,假如你创建一个非静态的引用,那么这个引用属于实例,每个实例都有,那么使用这个引用加锁,就相当于使用对象锁加锁的效果。

  • 多线程对类的同一个实例的方法的同步,都属于对象锁的范畴。
  • -----------------------------------------------------------
  • 实例锁synchronized(this),锁的对象是this,是类的实例。
  • 引用锁synchronized(object),锁的对象是object。
  • 类锁sycronized(Account.class),加在类对象上.当sell加了sycronized(Account.class),则Account实例对象a和b调用sell(),都需要得到同一把Account类锁
  • -----------------------------------------------------------
  • 若线程A持有了Account类锁,线程B又去持有a实例的实例锁,这是允许的并不冲突
  • syncronized修饰实例方法和代码块加锁的是当前实例对象this,静态方法加锁的是类class.

原因在于,synchronized(this)的锁对象是this,如果使用这种方式,一旦锁对象(实例)被别人获取,别人只要开个线程进行死循环 ,那你的Thread 1,这个正常的工作线程,就永远得不到执行,造成死锁。synchronized(object),锁的对象是object。所以,你拿到了sTest的实例锁,也不会影响到Thread1的正常执行。

/**

* @author 伍六七

* @date 2022/8/1 18:44

*/

//方式二--this---无输出

class STest{

   public void print(){

       synchronized (this){

           System.out.println("xxxx");

       }

   }

}

//方式一--Object---成功输出

class STest{

   private final Object object = new Object();

   public void print(){

       synchronized (object){

           System.out.println("xxxx");

       }

   }

}

public class SynchronizeMain {


   public static void main(String[] args) throws InterruptedException {


       STest sTest = new STest();


       // Thread 1

       Thread t1 = new Thread(() -> {

           sTest.print();

       });


       // Thread 2

       Thread t2 = new Thread(() -> {


           try {  //这里拿到的是实例锁

               synchronized (sTest){//当STest是引用锁时,采用反射使用sTest的同一把锁

                   while (true);

               }

           } catch (Exception e) {

               System.out.println("Exception="+e.getMessage());

           }


       });


       t2.start();

       Thread.sleep(1000);

       t1.start();

   }

}

案例

卖票

public class ExerciseSell {

   public static void main(String[] args) {

       TicketWindow ticketWindow = new TicketWindow(2000);

       List<Thread> list = new ArrayList<>();

       // 用来存储买出去多少张票

       List<Integer> sellCount = new Vector<>();

       for (int i = 0; i < 2000; i++) {

           Thread t = new Thread(() -> {

               // 分析这里的竞态条件

               int count = ticketWindow.sell(randomAmount());

               sellCount.add(count);

           });

           list.add(t);

           t.start();

       }

       list.forEach((t) -> {

           try {

               t.join();

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       });

       // 买出去的票求和

       log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());

       // 剩余票数

       log.debug("remainder count:{}", ticketWindow.getCount());

   }

   // Random 为线程安全

   static Random random = new Random();

   // 随机 1~5

   public static int randomAmount() {

       return random.nextInt(5) + 1;

   }

}


class TicketWindow {

   private int count;

   public TicketWindow(int count) {

       this.count = count;

   }

   public int getCount() {

       return count;

   }

   public int sell(int amount) {

       if (this.count >= amount) {

           this.count -= amount;

           return amount;

       } else {

           return 0;

       }

   }

}


转账

public class ExerciseTransfer {

   public static void main(String[] args) throws InterruptedException {

       Account a = new Account(1000);

       Account b = new Account(1000);

       Thread t1 = new Thread(() -> {

           for (int i = 0; i < 1000; i++) {

               a.transfer(b, randomAmount());

           }

       }, "t1");

       Thread t2 = new Thread(() -> {

           for (int i = 0; i < 1000; i++) {

               b.transfer(a, randomAmount());

           }

       }, "t2");

       t1.start();

       t2.start();

       t1.join();

       t2.join();

       // 查看转账2000次后的总金额

       log.debug("total:{}",(a.getMoney() + b.getMoney()));

   }

   // Random 为线程安全

   static Random random = new Random();

   // 随机 1~100

   public static int randomAmount() {

       return random.nextInt(100) +1;

   }

}

class Account {

   private int money;

   public Account(int money) {

       this.money = money;

   }

   public int getMoney() {

       return money;

   }

   public void setMoney(int money) {

       this.money = money;

   }

   public void transfer(Account target, int amount) {

       if (this.money > amount) {

           this.setMoney(this.getMoney() - amount);

           target.setMoney(target.getMoney() + amount);

       }

   }

}


死锁


jconsole

因此注意:主线程结束,进程不一定结束(子线程还在运行)

目录
相关文章
|
4月前
操作系统的主要功能
操作系统的主要功能
|
6月前
|
消息中间件 缓存 算法
从0开始回顾操作系统---系列二
进程管理 1、进程和线程的区别? 进程:一个正在运行中的程序就是一个进程,进程包括程序段、数据段、PCB三部分。 ● 程序段就是程序的代码; ● 数据段就是程序运行时产生的数据(比如全局变量、局部变量等); ● PCB中包含操作系统对其进行管理的各种信息(如进程标识符PID,进程当前状态,进程优先级) 线程:线程是属于进程的,是一个基本的 CPU 执行单元,是程序执行流的最小单元。 主要区别如下: ● 调度:进程是资源分配的基本单位,线程是程序执行的基本单位。 ● 开销:线程上下文切换开销小,进程线程间切换开销大。 ● 切换: 同一进程间线程切换不会引起进程切换,不同进程间线程切换会引
|
6月前
|
算法 安全 程序员
从0开始回顾操作系统---系列五
文件管理 1、虚拟文件系统? ● 文件系统的种类众多,而操作系统希望对用户提供一个统一的接口,于是在用户层与文件系统层引入了中间层,这个中间层就称为虚拟文件系统(Virtual File System,VFS)。 ● VFS 定义了一组所有文件系统都支持的数据结构和标准接口,这样程序员不需要了解文件系统的工作原理,只需要了解 VFS 提供的统一接口即可。 2、硬链接和软链接有什么区别? ● 硬链接就是在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。删除任意一个条目,文件还是存在,只要引用数量不为 0。但是硬链接有限制,它不能跨越文件系统,也不
|
6月前
|
算法
从0回顾操作系统---系列三
死锁与活锁 1、什么是死锁?死锁产生的条件? 什么是死锁: ● 在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。 ● 通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。 死锁产生的四个必要条件:(有一个条件不成立,则不会产生死锁) ● 互斥条件:一个资源一次只能被一个进程使用; ● 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放; ● 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺; ● 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关
|
6月前
|
存储 Java
从0开始回顾操作系统---系列六
IO多路复用 1、讲一讲IO多路复用? IO多路复用模型指的是:使用单个进程/线程同时处理多个网络连接IO,他的原理就是select、poll、epoll 不断轮询所负责的所有 socket,当某个socket有数据到达了,就通知用户进程。该模型的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。 2、select、poll 和 epoll 之间的区别 1. select:时间复杂度 O(n) ● select 仅仅知道有 I/O 事件发生,但并不知道是哪几个流,所以只能无差别轮询所有流,找出能读出数据或者写入数据的流,并对其进行操作。所以 select 具有 O(n) 的无差
|
6月前
|
调度 数据安全/隐私保护 数据格式
从0开始回顾操作系统---系列一
概述 1、什么是操作系统 ● 操作系统简称OS,负责管理协调计算机硬件与软件资源工作的系统软件。 ● 屏蔽了硬件层的复杂性,为上层应用软件与用户提供易用的服务。 2、操作系统主要有哪些功能? 操作系统最主要的功能: ● 处理器(CPU)管理:CPU的管理和分配,主要指的是进程管理。 ● 内存管理:内存的分配和管理,主要利用了虚拟内存的方式。 ● 外存管理:外存(磁盘等)的分配和管理,将外存以文件的形式提供出去。 ● I/O管理:对输入/输出设备的统一管理。 除此之外,还有保证自身正常运行的健壮性管理,防止非法操作和入侵的安全性管理。 3、什么是系统调用?有哪几类? 根据进程访问资源的特点,
|
6月前
|
存储 算法 C语言
从0开始回顾操作系统---系列四
内存管理 1、什么是内存管理 操作系统对内存的划分和动态分配就是内存管理,主要可以分为: ● 内存空间的扩充(实现虚拟性) ● 内存空间的分配与回收 ● 地址转换:操作系统负责实现逻辑地址到物理地址的转换 ● 存贮保护:保证各进程在自己的内存空间内运行,不会越界访问 2、内存管理机制和内存管理方式有哪些? 1. 分块管理 ● 是连续管理的一种,把内存分为几个大小相等且固定的块,每个进程占用其中一个,如果进程很小的话,会浪费大量的空间。已经淘汰。 2. 分页管理 ● 把内存分为若干个很小的页面,相对比分块的划分力度更大一些。提高内存利用率。减少碎片,页式管理通过页表对应逻辑地址和物理地址。
|
6月前
|
Linux Android开发 iOS开发
操作系统的作用与常见操作系统介绍
操作系统的作用与常见操作系统介绍
|
6月前
|
存储 编译器 Linux
操作系统相关问题——应用程序和操作系统怎么配合
应用程序和操作系统都是软件, CPU会将它们一视同仁,甚至CPU不知道自己在执行的程序是操作系统还是一般应用软件。CPU只知道去cs:ip寄存器中指向的内存取出指令并执行,它不知道什么是操作系统。
|
存储 Unix Linux
操作系统和内核有什么区别?
操作系统和内核有什么区别?
1367 0
操作系统和内核有什么区别?
下一篇
无影云桌面