并发编程基础知识篇--线程的状态和基本操作

news2024/9/24 3:30:02

目录

创建线程的四种方式

线程的状态和生命周期

扩展知识

线程的调度

线程状态的基本操作

interrupted

实例

join

实例 

sleep

实例

扩展小知识

yield

实例 

扩展


创建线程的四种方式

创建线程的四种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable和Future创建线程
  4. 使用Executor框架创建线程池

创建线程的具体实现可以参考Java进阶篇--创建线程的四种方式

线程的状态和生命周期

在Java中,任何对象都有生命周期,线程也不例外,它也有自己的生命周期。当Thread对象创建完成时,线程的生命周期便开始了。当run()方法中代码正常执行完毕或者线程抛出一个未捕获的异常(Exception)或者错误(Error)时,线程的生命周期便会结束。线程整个生命周期可以分为五个阶段,分别是新建状态(New)就绪状态(Runnable)运行状态(Running)阻塞状态(Blocked)死亡状态(Terminated),线程的不同状态表明了线程当前正在进行的活动。

在程序中,通过一些操作,可以使线程在不同状态之间转换。

上图展示了线程各种状态的转换关系,箭头表示可转换的方向,其中,单箭头表示状态只能单向的转换,例如,线程只能从新建状态转换到就绪状态,反之则不能;双箭头表示两种状态可以互相转换,例如,就绪状态和运行状态可以互相转换。

接下来针对线程生命周期中的五种状态分别进行详细讲解,具体如下:

1.新建状态(New)

创建一个线程对象后,该线程对象就处于新建状态,此时它不能运行,和其他Java对象一样,仅仅由Java虚拟机为其分配了内存,没有表现出任何线程的动态特征。

2.就绪状态(Runnable)

当线程对象调用了start()方法后,该线程就进入就绪状态。处于就绪状态的线程位于线程队列中,此时它只是具备了运行的条件,能否获得CPU的使用权并开始运行,还需要等待系统的调度。

3.运行状态(Running)

如果处于就绪状态的线程获得了CPU的使用权,并开始执行run()方法中的线程执行体,则该线程处于运行状态。一个线程启动后,它可能不会一直处于运行状态,当运行状态的线程使用完系统分配的时间后,系统就会剥夺该线程占用的CPU资源,让其他线程获得执行的机会。需要注意的是,只有处于就绪状态的线程才可能转换到运行状态。

4.阻塞状态(Blocked)

一个正在执行的线程在某些特殊情况下,如被人为挂起或执行耗时的输入/输出操作时,会让出CPU的使用权并暂时中止自己的执行,进入阻塞状态。线程进入阻塞状态后,就不能进入排队队列。只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

5.死亡状态(Terminated)

当线程调用stop()方法或run()方法正常执行完毕后,或者线程抛出一个未捕获的异常(Exception)、错误(Error),线程就进入死亡状态。一旦进入死亡状态,线程将不再拥有运行的资格,也不能再转换到其他状态。

扩展知识

线程在运行状态与阻塞状态之间的转换是由于特定的条件和操作引起的。下面是一些线程从运行状态转换为阻塞状态的常见原因:

  1. 等待I/O:当线程执行需要等待输入/输出操作完成时,例如读取文件、网络通信等,它会被阻塞。
  2. 获得锁失败:当线程尝试获取一个被其他线程持有的锁时,它会被阻塞,直到锁可用。
  3. 等待其他线程完成:线程可能需要等待其他线程执行完特定的操作或达到特定的条件,才能继续执行。
  4. 调用 sleep() 方法:线程可以调用 Thread.sleep() 方法进入阻塞状态,暂停一段指定时间。
  5. 调用 wait() 方法:线程可以在对象上调用 wait() 方法,使其进入阻塞状态,直到其他线程调用相同对象的 notify() 或 notifyAll() 方法。

要将线程从阻塞状态转换为就绪状态,需要满足特定的条件或操作:

  1. I/O 操作完成:当线程所需的输入/输出操作完成时,它会从阻塞状态返回到就绪状态。
  2. 锁可用:当线程等待的锁变为可用时,它会从阻塞状态返回到就绪状态,并尝试再次获取锁以继续执行。
  3. 其他线程通知:当其他线程调用相同对象的 notify() 或 notifyAll() 方法时,等待该对象的线程会从阻塞状态返回到就绪状态,然后竞争获得对象的锁以继续执行。
  4. sleep() 时间到期:当调用 Thread.sleep() 方法的线程休眠时间到期时,它会从阻塞状态返回到就绪状态。
  5. wait() 被唤醒:当其他线程调用相同对象的 notify() 或 notifyAll() 方法,或者调用 wait(long timeout) 的时间到期时,等待该对象的线程会从阻塞状态返回到就绪状态。

注意:线程从阻塞状态只能进入就绪状态,而不能直接进入运行状态,也就是说结束阻塞的线程需要重新进入可运行池中,等待系统的调度。

线程的调度

在计算机系统中,线程调度是操作系统或虚拟机对线程执行顺序和运行时间的管理。

  1. 分时调度模型:在这种模型中,所有的线程会轮流地获得CPU的使用权。每个线程会被分配一个时间片,当这个时间片用完之后,下一个线程就会获得CPU的使用权。这种模型的优点是可以让每个线程都有机会运行,从而实现公平的资源分配。然而,如果一个线程需要长时间运行,那么其他线程就需要等待它完成,这可能会导致程序的响应速度变慢。
  2. 抢占式调度模型:在这种模型中,操作系统会选择优先级高的线程来运行。如果线程的优先级相同,那么操作系统会随机选择一个线程来运行。当这个线程失去了CPU的使用权后,操作系统会选择另一个线程来运行。这种模型的优点是可以提高程序的响应速度,因为优先级高的线程可以更快地获得CPU的使用权。然而,如果一个线程长时间运行,那么其他线程就需要等待它完成,这可能会导致资源的浪费。
  3. Java虚拟机的调度模型:Java虚拟机默认采用抢占式调度模型。它使用了优先级调度算法来决定线程的执行顺序,并且可以通过设置线程的优先级来影响调度的结果。Java提供了一些方法来控制线程的调度,如Thread.yield() 方法可以让当前线程主动放弃CPU的使用权,以便给其他线程执行的机会。

注意:通常情况下,程序员不需要关心这个过程,因为Java虚拟机会自动处理。但是,在一些特定的需求下,可能需要改变这种模式,由程序自己来控制CPU的调度。这可以通过使用Java的Thread类和相关的API来实现。

线程状态的基本操作

interrupted

中断是一种协作机制,用于在多线程环境中控制程序的执行。中断标志位是一种内部状态,用于表示线程是否被中断。当一个线程被中断时,中断标志位将被设置为true,并且会抛出InterruptedException异常。

  1. public void interrupt():用于中断该线程对象。调用该方法会将线程的中断标志位设置为true。如果该线程被调用了Object wait()、Object wait(long)、Thread.sleep(long)、Thread.join()、Thread.join(long)等方法时,会抛出InterruptedException并清除中断标志位。
  2. public boolean isInterrupted():用于测试该线程对象是否被中断。调用该方法可以检查当前线程的中断状态,并返回一个boolean值,表示线程是否被中断。调用该方法不会清除中断标志位。
  3. public static boolean interrupted():用于测试当前线程是否被中断。调用该静态方法可以检查当前线程的中断状态,并返回一个boolean值,表示当前线程是否被中断。调用该方法会清除中断标志位(即将中断标志位设置为false)。

在使用这些方法时需要注意以下几点:

  1. 调用interrupt()方法会将线程的中断标志位设置为true。
  2. 被中断的线程可以通过调用isInterrupted()方法来感知其他线程对其自身的中断操作,并做出相应的响应。
  3. 当抛出InterruptedException时,会清除线程的中断标志位。
  4. 调用interrupted()方法会清除当前线程的中断标志位。

实例

下面结合具体的实例来看一看

public class Main {
    public static void main(String[] args) {
        Thread myThread = new MyThread();
        myThread.start(); // 启动线程

        try {
            Thread.sleep(2000); // 主线程休眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        myThread.interrupt(); // 中断线程
    }

    private static class MyThread extends Thread {
        @Override
        public void run() {
            while (!isInterrupted()) {
                System.out.println("线程正在运行...");
                try {
                    Thread.sleep(500); // 线程休眠500毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break; // 捕获到InterruptedException时退出循环,结束线程执行
                }
            }
            System.out.println("线程已经中断...");
        }
    }
}

输出结果 

线程正在运行...
线程正在运行...
线程正在运行...
线程正在运行...
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at 练习.Main$MyThread.run(Main.java:22)
线程已经中断...

在上面的示例中,我们创建了一个继承自Thread类的MyThread类,它重写了run()方法来定义线程的执行逻辑。在run()方法中,我们通过检查线程的中断状态(isInterrupted())来决定是否退出循环。同时,在每次循环中,线程会休眠500毫秒(Thread.sleep(500))。

在Main类的main()方法中,我们创建了一个MyThread的实例,并调用start()方法开启线程。主线程随后休眠2秒,然后调用myThread.interrupt()方法中断线程。当线程被中断时,它将捕获到InterruptedException并退出循环,最后输出一条提示信息。

因此,中断操作可以看做线程间一种简便的交互方式。一般在结束线程时通过中断标志位或者标志位的方式可以有机会去清理资源,相对于武断而直接的结束线程,这种方式要优雅和安全。

join

join()是Java中的一个方法,它用于让一个线程等待另一个线程执行完成。当一个线程调用另一个线程的join()方法时,它将会被阻塞,直到被调用的线程执行完毕。

join()方法有以下几种重载形式:

  1. public final void join() throws InterruptedException:当前线程调用另一个线程的join()方法,会使当前线程进入阻塞状态,直到被调用的线程执行完成。如果被调用的线程发生中断,则会抛出InterruptedException异常。
  2. public final synchronized void join(long millis) throws InterruptedException:当前线程调用另一个线程的join(long millis)方法,会使当前线程进入阻塞状态,最多等待指定的时间(毫秒)。如果被调用的线程在指定的时间内执行完毕,则当前线程恢复运行;如果超过指定的时间仍未执行完毕,当前线程也会恢复运行。
  3. public final synchronized void join(long millis, int nanos) throws InterruptedException:与上一种形式类似,但允许指定纳秒级别的额外等待时间。

通过使用join()方法,可以实现多个线程之间的协同工作和结果的合并。例如,主线程可以调用某个子线程的join()方法来等待子线程执行完毕,然后再继续执行主线程的后续逻辑。

实例 

下面是一个简单示例,演示了join()方法的用法:

public class Main {
    public static void main(String[] args) {
        Thread thread1 = new MyThread("Thread 1");
        Thread thread2 = new MyThread("Thread 2");

        thread1.start();
        thread2.start();

        try {
            thread1.join(); // 主线程等待thread1执行完成
            thread2.join(); // 主线程等待thread2执行完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("所有线程都已完成.");
    }

    private static class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            System.out.println(getName() + " 已启动.");
            try {
                Thread.sleep(2000); // 线程休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(getName() + " 已完成.");
        }
    }
}

输出结果为:

Thread 1 已启动.
Thread 2 已启动.
Thread 1 已完成.
Thread 2 已完成.
所有线程都已完成.

sleep

在Java中,sleep()方法用于使当前线程休眠(暂停执行)一段时间。它是Thread类的静态方法,可以通过线程对象或直接通过类名调用。

sleep()方法有两种重载形式:

  1. sleep(long millis)这种形式表示当前线程休眠指定的毫秒数。
  2. sleep(long millis, int nanos)这种形式表示当前线程休眠指定的毫秒数和纳秒数。纳秒数范围是0到999999。

使用sleep()方法时需要处理InterruptedException异常,因为其他线程调用了当前线程的interrupt()方法会中断当前线程的休眠。

实例

下面是一个简单的示例,演示了如何使用sleep()方法:

public class Main {
    public static void main(String[] args) {
        System.out.println("主线程开始执行");

        try {
            Thread.sleep(2000); // 当前线程休眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程继续执行");
    }
}

输出结果可能如下所示:

主线程开始执行  //(等待2秒)
主线程继续执行

通过sleep()方法,我们可以控制线程的暂停时间,用于实现一些需要等待一段时间后再执行的逻辑。

需要注意的是,sleep()方法不会释放对象锁,因此其他线程无法获得被当前线程持有的锁。如果在多线程环境下使用sleep()方法,需要注意并发访问共享资源的同步问题。

扩展小知识

sleep方法经常拿来与Object.wait()方法进行比较,这也是面试经常被问的地方。

sleep()方法和wait()方法在Java中用于不同的目的,尽管它们都可以暂停线程的执行,但有一些重要的区别。

相同点:

  • 都可以使当前线程暂停执行一段时间。

区别点:

1.来源和使用方式:

  • sleep()方法是Thread类的静态方法,可以直接通过线程对象或类名调用。它用于在指定的时间段内使当前线程休眠。
  • wait()方法是Object类的实例方法,必须在同步代码块/方法中使用,通过调用对象的wait()方法来暂停当前线程,同时释放对象锁。

2.调用位置:

  • sleep()方法可以在任意位置的代码中调用,无需获得对象锁。
  • wait()方法必须在同步代码块/方法中调用,因为它会释放对象锁,并等待被其他线程通过notify()、notifyAll()方法唤醒。

3.被唤醒机制:

  • sleep()方法在指定的时间到达后自动唤醒当前线程,然后该线程进入就绪状态等待CPU时间片。其他线程无法直接唤醒通过sleep()方法暂停的线程。
  • wait()方法需要等待其他线程调用相同对象的notify()或notifyAll()方法来唤醒当前线程。

4.锁的释放:

  • sleep()方法不释放对象锁,即使线程休眠,其他线程仍无法获得被当前线程持有的锁。
  • wait()方法会释放对象锁,让其他线程进入对象的同步代码块/方法。

综上所述,sleep()方法主要用于线程的时间调度和暂停执行一段时间,而wait()方法主要用于线程间的协作和等待特定条件满足后再继续执行。选择使用哪种方法取决于具体的需求和场景。

yield

yield()是Java中的一个方法,它用于提示线程调度器当前线程愿意放弃对CPU的使用权。当一个线程调用yield()方法时,它就会让出自己的时间片,告诉调度器可以先执行其他优先级相同或更高的线程。

1.调用方式:

  • yield()方法是Thread类的静态方法,可以直接通过线程对象或类名调用。
  • 例如,Thread.yield(); 或者 Thread.currentThread().yield();

2.功能和作用:

  • yield()的作用是暂停当前正在执行的线程,并给予其他等待线程执行的机会。
  • 调用yield()方法的线程进入就绪状态,等待调度器重新选择执行。

3.调度器行为:

  • 调用yield()方法不保证当前线程会立即暂停执行,也无法保证其他线程会立即得到执行。
  • 具体的调度器行为取决于操作系统和Java虚拟机的实现,可能有一定的优化策略。

4.适用场景:

  • yield()方法常用于协助线程间的合理调度,尤其在具有相同优先级且需要公平共享CPU资源的情况下。
  • 它可以用于避免某个线程过度占用CPU资源,提高系统的整体性能和公平性。

需要注意的是,yield()方法不能保证在多线程程序中达到精确的任务调度顺序。它只是一种提示机制,告诉调度器当前线程有一定的让步意愿。实际上,调度器可以忽略这个提示而继续执行当前线程。

总结:yield()方法允许当前线程主动放弃对CPU的使用权,以促进其他线程的执行。然而,由于具体的调度行为取决于操作系统和虚拟机的实现,因此不应将yield()方法作为实现严格的线程间协作和任务调度顺序的方式。

另外需要注意的是,sleep()和yield()方法,同样都是当前线程会交出处理器资源,而它们不同的是,sleep()交出来的时间片其他线程都可以去竞争,也就是说都有机会获得当前线程让出的时间片。而yield()方法只允许与当前线程具有相同优先级的线程能够获得释放出来的CPU时间片。

实例 

以下是一个简单的Java代码示例,演示了如何使用yield()方法:

public class Main implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);

            // 使用yield()方法让出CPU资源
            Thread.yield();
        }
    }

    public static void main(String[] args) {
        // 创建两个线程对象
        Thread thread1 = new Thread(new Main());
        Thread thread2 = new Thread(new Main());

        // 启动两个线程
        thread1.start();
        thread2.start();
    }
}

在上述示例中,我们创建了一个名为YieldExample的类,实现了Runnable接口。在run()方法中,每个线程都会打印数字1到5,并在每次循环后调用yield()方法来让出CPU资源。

在main()方法中,我们创建了两个线程对象并启动它们。由于线程调度器的具体行为无法确定,因此无法预测哪个线程在某一次循环中会先执行。然而,通过使用yield()方法,我们鼓励线程之间进行公平的CPU资源共享。

请注意,由于线程调度的不确定性,运行示例代码可能会产生不同的输出结果。可以通过多次运行代码来观察这种不确定性和共享CPU资源的行为。

扩展

线程状态的基本操作还包括以下几种:

  1. start:启动线程,调用线程对象的start()方法。
  2. run:在start()方法调用后,线程进入可运行状态,当线程调度选中该线程时,run()方法开始执行。
  3. resume:恢复阻塞状态的线程,使该线程回到就绪状态。
  4. block:将当前线程放入一个阻塞队列中,进入阻塞状态。
  5. newLock:获取新的锁,使线程进入等待状态,直到获得锁为止。
  6. lock:获取锁,如果锁已经被其他线程持有,则当前线程进入等待状态,直到获得锁为止。
  7. unlock:释放锁,使等待该锁的线程能够获得锁并继续执行。
  8. destroy:销毁线程对象,使该线程处于死亡状态。

这些操作都是线程状态转换的基本操作,可以帮助我们更好地管理和控制线程的执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/935787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

博客系统——前端部分

目录 一、博客页面介绍 二、实现博客列表页 1、先实现导航栏 2、页面主体 左侧区域的实现&#xff1a;​编辑 右侧页面的实现&#xff1a;​编辑 博客列表页代码汇总&#xff1a; 三、实现博客详情页 代码实现&#xff1a; 四、实现博客登录页​编辑 五、博客编辑页 …

【赋权算法】Python实现熵权法

在开始之前&#xff0c;我们先说一下信息熵的概念。 当一件事情发生&#xff0c;如果是意料之中&#xff0c;那么这个事情就并不能拿来当做茶余饭后的谈资&#xff0c;我们可以说这个事情并没有什么信息和价值。而当一件不可能发生的事情发生的时候&#xff0c;我们可能就会觉…

挖数据四周年庆典,壕礼不断,惊喜不停!

挖数据四周岁啦&#xff01;为了感谢广大用户们一路以来的支持与陪伴&#xff0c;我们特地准备了丰富的优惠活动&#xff0c;希望能够用最实际的行动来回馈您们的厚爱。四年的成长与蜕变&#xff0c;都是因为有您们的陪伴与鼓励&#xff0c;我们期待与您们一同分享这份喜悦与成…

Linux 基金会宣布正式进驻中国

在 LinuxCon 2017 &#xff08;北京&#xff09;即将召开前夕&#xff0c;我们Linux 中国会同 51CTO、开源中国对 Linux 基金会执行董事 Jim Zemlin 进行了一场远跨大洋的视频专访。 在这次专访中&#xff0c;Jim 先生回答了几个开源界和互联网领域关注的问题&#xff0c;并披…

PCI设备和PCI桥的配置空间(header_type0、header_type1)和配置命令(type0、type1)详解

1、PCI典型拓扑 2、type0和type1 名称含义Bus Number设备所在总线号Device Number设备分配到的设备号Function Number功能号&#xff0c;有的设备是支持多个功能的&#xff0c;最多8种功能Register Number要访问的寄存器地址 (1)type0和type1的区别&#xff1a;AD[1:0]是00代表…

几个nlp的小任务(生成式任务——语言模型(CLM与MLM))

@TOC 本章节需要用到的类库 微调任意Transformers模型(CLM因果语言模型、MLM遮蔽语言模型) CLM MLM 准备数据集 展示几个数据的结构

【AI底层逻辑】——篇章7(下):计算资源软件代码共享

续上篇... 目录 续上篇... 三、计算资源 1、第一阶段&#xff1a;数据大集中 2、第二阶段&#xff1a;资源云化 ①“云”的分类 ②虚拟化技术 ③边缘计算的普及 四、软件代码共享 总结 往期精彩&#xff1a; 三、计算资源 AlphaGo算法论文虽然已经发表&#xff0c;但…

华为OD七日集训第2期 - 按算法分类,由易到难,循序渐进,玩转OD(文末送书)

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第2期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、逻辑分析第2天、字符串处理第3天、数据结构第4天、递归回溯第5天、二分查找第6天、深度优先搜索dfs算法第7天、动态规划 六、集训总结1、《代码…

rke安装k8s

1、修改集群中各物理机主机名hostname文件 # 查看 cat /etc/hostname # 命令修改 hostnamectl set-hostname k8s-master2、实现主机名与ip地址解析 # 查看cat /etc/hosts # 修改 vi /etc/hosts3、配置ip_forward过滤机制 # 修改 vi /etc/sysctl.conf net.ipv4.ip_forward1…

RT-Thread IO设备模型

IO设备模型 RTT提供了一套简单的I/O设备模型框架&#xff0c;它位于硬件和应用程序之间&#xff0c;共分成三层&#xff0c;从上到下分别是I/O设备管理层、设备驱动框架层、设备驱动层。 应用程序通过I/O设备管理接口获得正确的设备驱动&#xff0c;然后通过这个设备驱动与底层…

递归算法学习——全排列

目录 ​编辑 一&#xff0c;问题描述 1.例子&#xff1a; 题目接口&#xff1a; 二&#xff0c;问题分析和解决 1.问题分析 2.解题代码 一&#xff0c;问题描述 首先我们得来先看看全排列的问题描述。全排列问题的问题描述如下&#xff1a; 给定一个不含重复数字的数组 n…

DTC状态变化例子 4

例子1&#xff1a; 此示例概述了两个操作周期排放相关的 OBD DTC 中 DTC 状态位的操作。该图显示了两个操作周期排放相关的 OBD DTC 的处理。该处理也可应用于非排放相关的 OBD DTC&#xff0c;此处显示仅供一般参考。 0 接收到清除诊断信息 → DTC 状态字节初始化。 1, 2 相关…

基于类电磁机制算法优化的BP神经网络(预测应用) - 附代码

基于类电磁机制算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于类电磁机制算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.类电磁机制优化BP神经网络2.1 BP神经网络参数设置2.2 类电磁机制算法应用 4.测试结果&…

RabbitMQ---订阅模型-Topic

订阅模型-Topic • Topic类型的Exchange与Direct相比&#xff0c;都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符&#xff01; • Routingkey 一般都是有一个或多个单词组成&#xff0c;多个单词之间以…

【clojure】入门篇-01

一、环境的配置 1.java环境配置 clojureScript 需要java环境的配置需要下载jdk进行java环境变量配置 下载官网 java环境变量的配置教程 2.Leningen环境配置 1.下载.bat文件内容 2.配置环境变量 2.8.3及以上内容进行配置 lein教程 2.使用vscode vscode官网 下载插件 C…

SIP 协议路由规则详解

文章目录 SIP 路由关键字段SIP 路由图解 SIP 路由关键字段 SIP 协议实际上和 HTTP 类似&#xff0c;都是基于文本、可阅读的应用层协议&#xff0c;二者的不同之处在于 SIP 协议是有状态的。在 SIP 协议中&#xff0c;影响报文路由的相关字段如下表所示&#xff0c;总结起来如…

给微软.Net runtime运行时提交的几个Issues

前言 因为目前从事的CLRJIT,所以会遇到一些非常底层的问题&#xff0c;比如涉及到微软的公共运行时和即时编译器或者AOT编译器的编译异常等情况,这里分享下自己提的几个Issues。原文:微软.Net runtime运行时提交的几个Issues Issues 一.issues one 第一个System.Numerics.Vecto…

深度强化学习。介绍。深度 Q 网络 (DQN) 算法

马库斯布赫霍尔茨 一. 引言 深度强化学习的起源是纯粹的强化学习&#xff0c;其中问题通常被框定为马尔可夫决策过程&#xff08;MDP&#xff09;。MDP 由一组状态 S 和操作 A 组成。状态之间的转换使用转移概率 P、奖励 R 和贴现因子 gamma 执行。概率转换P&#xff08;系统动…

SaaS多租户系统架构设计

前言&#xff1a;多租户是SaaS&#xff08;Software-as-a-Service&#xff09;下的一个概念&#xff0c;意思为软件即服务&#xff0c;即通过网络提供软件服务。SaaS平台供应商将应用软件统一部署在自己的服务器上&#xff0c;客户可以根据工作的实际需求&#xff0c;通过互联网…

万字长文解析AQS抽象同步器核心原理(深入阅读AQS源码)

AQS抽象同步器核心原理 在争用激烈的场景下使用基于CAS自旋实现的轻量级锁有两个大的问题&#xff1a; CAS恶性空自旋会浪费大量的CPU资源。在SMP架构的CPU上会导致“总线风暴”。 解决CAS恶性空自旋的有效方式之一是以空间换时间&#xff0c;较为常见的方案有两种&#xff…