9.Java内置锁的核心原理-Synchronized

news2024/11/27 21:08:11

文章目录

  • Java内置锁的核心原理-Synchronized
    • 1.线程安全问题
      • 1.1.自增运算分析
      • 1.2.临界区资源和临界区代码片段
    • 2.synchronized关键字
      • 2.1.synchronized同步方法
      • 2.2.synchronized同步代码块
      • 2.3.synchronized同步方法和synchronized同步代码块区别
      • 2.4.静态的同步方法
      • 2.5.内置锁的释放

Java内置锁的核心原理-Synchronized

Java内置锁是一个互斥锁,这就意味着多个线程只有一个线程能够获取到该锁,当线程B尝试获取A持有的内置锁时,B就必须阻塞或者等待,直到A线程释放这个锁,不然B线程就是会一直等待下去。

Java中的每个对象都可以用作锁,这些锁被称为内置锁,线程进入同步代码块或者方法时,会自动获得该锁,在退出同步代码块或者方式时,会自动释放该锁。获得内置锁的唯一途径就是进入这个锁保护的同步代码块或者同步方法。

Java内置锁是指在Java语言中提供的一种同步机制,用于在多线程环境下保护共享资源的访问。它是通过关键字synchronized来实现的。

Java内置锁的实现涉及到以下几个方面:

  1. 对象监视器(Object Monitor):每个Java对象都与一个对象监视器关联。对象监视器是用于实现同步的基本结构,可以看作是一个互斥锁。每个对象监视器都有一个相关的等待集(wait set)和一个锁持有者。
  2. 互斥性(Mutual Exclusion):Java内置锁提供了互斥性,即同一时间只能有一个线程持有对象监视器的锁。当一个线程获取到锁后,其他线程就无法进入被锁保护的代码块,只能等待锁的释放。
  3. 可重入性(Reentrancy):Java内置锁是可重入的,也就是同一个线程可以多次获取同一个对象监视器的锁。这种机制允许线程在持有锁的情况下再次进入被锁保护的代码块,而不会被自己所持有的锁所阻塞。
  4. 等待与通知机制(Wait and Notify):Java内置锁提供了等待与通知机制,允许线程在获取锁之后,如果条件不满足,可以主动释放锁并进入等待状态,直到其他线程通知条件发生变化后再次竞争获取锁。这个机制通过wait()notify()notifyAll()等方法实现。
  5. 内存可见性(Memory Visibility):Java内置锁通过加锁和解锁的操作,可以确保共享变量的可见性。当一个线程释放锁时,会将对共享变量的更新刷新到主内存中,使得其他线程可以看到最新的值。

Java内置锁的使用方式是通过synchronized关键字来修饰代码块或方法。当一个线程进入被synchronized修饰的代码块或方法时,它会尝试获取对象监视器的锁,如果锁已经被其他线程持有,则该线程会进入阻塞状态,直到锁被释放。一旦线程获取到锁,就可以执行被锁保护的代码,并且在执行完毕后释放锁,以允许其他线程进入。

下面我们先从线程安全问题看起,逐渐了解Java内置锁的核心原理

1.线程安全问题

在Java中,线程安全问题是由多线程并发访问共享资源而引起的。在多线程环境中,多个线程可以同时访问和修改共享数据,而不同线程之间的执行顺序是不确定的。这种并发访问可能导致数据的不一致性、错误的计算结果以及线程间的竞争条件。

线程安全问题的出现主要是由于以下几个原因:

  1. 共享数据:当多个线程同时访问和修改同一个共享数据时,就会引发线程安全问题。共享数据可以是全局变量、静态变量、对象的成员变量等。
  2. 竞态条件:竞态条件是指多个线程在执行过程中,由于执行顺序的不确定性而导致结果的正确性依赖于线程执行顺序的问题。竞态条件可能会导致数据的不一致性和错误的计算结果。
  3. 数据竞争:数据竞争是指多个线程同时访问共享数据,并且至少有一个线程对该数据进行了写操作,而没有适当的同步机制来保证线程之间的顺序和一致性。数据竞争可能导致数据的不一致性和未定义行为。
  4. 缺乏同步机制:在多线程环境中,如果没有适当的同步机制来保护共享资源的访问,就会导致线程之间的竞争条件和数据竞争,从而引发线程安全问题。

线程安全问题可能会导致程序的不可预测行为、数据的不一致性、性能下降以及安全漏洞。为了解决线程安全问题,需要采取适当的同步机制来保护共享资源的访问,例如使用锁、原子操作、同步容器、并发工具类等。此外,还需要注意避免死锁、活锁等并发陷阱,设计线程安全的类和方法,以及进行适当的测试和调优。

下面我们通过一个小实验来看下线程的安全问题

​ 我们使用10个线程,对一个共享变量进行自增运算,每个线程自增1W次,我们最终来看看下这个共享变量的结果

/**
 * 使用10个线程,对一个共享变量进行自增运算,每个线程自增1W次,我们最终来看看下这个共享变量的结果
 */
@Slf4j
public class TestAdd {
    private int count = 0;
    // private Object lock = new Object();

    @Test
    @DisplayName("测试自增")
    public void testAdd() {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> {
                for (int j = 0; j < 10000; j++) {
                    //synchronized (lock){
                    count++;
                    //}
                }
            });
        }
        //  休眠10s
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        executorService.shutdown();
        log.error("预期值:100000");
        log.error("最终结果:{}", count);

    }

}

在这里插入图片描述

1.1.自增运算分析

为什么自增运算不是线程安全的呢?实际上,一个自增运算符是一个符合操作,至少包括3个JVM指令,内存取值寄存器加1存值到寄存器,这三个指令在JVM中操作时独立进行的。

在现代计算机的CPU中,存在一个叫做流水线(pipeline)的机制。流水线可以同时执行多条指令,将指令的执行分为多个阶段,并行地处理不同指令的不同阶段。例如,流水线可以将指令的取指、译码、执行、访存和写回等阶段分开处理。

对于自增运算,它通常包括以下步骤

  1. 内存取值
  2. 寄存器加1
  3. 存值到寄存器

这个三个JVM指令是不可以在分的,他们都具有原子性,是线程安全的,但是两个或者两个以上原子操作结合在一起,那么就不具备原子性,不如先读后写,先写后读都有可能导致结果不一致

在单线程环境下,这些步骤会按顺序执行,并且每一步的结果都会影响下一步的执行。但在多线程环境下,多个线程可能同时执行自增操作,导致以下问题:

  1. 可见性问题:当一个线程执行自增操作时,它首先需要读取变量的当前值。然而,由于CPU的高速缓存和指令重排等优化机制,线程可能会从自己的缓存中读取变量的值,而不是从主内存中读取。这导致不同线程看到的变量值可能不一致,从而引发不正确的结果。
  2. 写回问题:在流水线中,写回操作可能会延迟执行,不会立即将结果写回主内存。这就意味着,当一个线程完成自增操作并将结果写回变量时,其他线程可能仍然在流水线中的前面阶段执行自增操作,导致结果的覆盖或丢失。

综上所述,自增运算在多线程环境下存在可见性问题和写回问题,这可能导致多个线程对同一个变量的自增操作相互干扰,导致不正确的结果。为了解决这个问题,可以使用同步机制(如锁或原子操作)来确保自增操作的原子性,或者使用线程安全的数据结构来避免竞态条件。

1.2.临界区资源和临界区代码片段

在后端开发中,我们常常认为代码会以线程的,串行的方式执行,容易忽视多个线程并发执行,从而导致意想不到的结果.下面我们了解并发中几个重要概念,临界区资源

临界区代码片段,互斥机制,竞态条件

  1. 临界区资源

    临界区资源是指在并发编程环境中由多个线程或进程共享的数据或资源

    • 临界区资源是被多个线程或进程共享的数据或资源。
    • 临界区资源包括共享变量、数据结构、文件、设备等。
    • 多个线程或进程同时访问临界区资源可能导致数据不一致或竞态条件。
  2. 临界区代码片段

    临界区代码片段是指程序中访问临界区资源的代码段。

    • 临界区代码片段通常是对共享资源进行读取、写入或修改的部分。
    • 需要使用互斥机制来保证对临界区资源的互斥访问。
    • 临界区代码片段需要被互斥锁(或其他互斥机制)保护。
    • 只有获得互斥锁的线程或进程才能执行临界区代码片段。
  3. 互斥机制

    互斥机制是用于实现对临界区资源的互斥访问的同步机制。

    • 互斥机制确保在任意时刻只有一个线程或进程能够访问临界区资源。
    • 常用的互斥机制包括互斥锁、信号量、条件变量等。
    • 互斥机制可以避免竞态条件和数据不一致的问题。
  4. 竞态条件:

    竞态条件是指在多线程或多进程环境中,由于不恰当的执行顺序而导致程序的行为不确定或产生错误的现象。

    • 多个线程或进程同时访问、读取或修改的共享数据或资源。
    • 多个线程或进程以无法预测的顺序并发执行,可能会相互干扰。
    • 缺乏适当的同步机制或同步策略,无法保证对共享资源的互斥访问。

在并发编程中,临界区资源是受到保护的对象的,临界区代码段是每个线程访问临界资源的代码段,多个线程必须互斥的进行临界区资源进行访问.

线程进入临界区代码段必须先申请资源,申请成功后才能进入临界区代码段,执行完毕释放资源,具体如图

在这里插入图片描述

上面的案例中,我们就是没有临界区代码进行保护,导致结果和我们的预期相差较大,导致多个线程同时访问一个资源时,出现了竞态条件。

为了避免出现竞态条件,我们必须保证,临界区代码段操作,必须具有互斥性或者称为排他性(同一时刻只有一个线程能进行操作),在Java中有很多方式可以使用,这里我们使用synchronized来对临界区代码段进行排他性保护。再次运行观察结果

private Object lock = new Object();

synchronized (lock){
    count++;
}

在这里插入图片描述

2.synchronized关键字

在Java开发中,线程同步使用的最多的就是synchronized关键字,每个Java对象都隐含一把锁,这个锁被称为Java内置锁(对象锁,隐式锁)。使用synchronized调用相当于获取这把内置锁,这样就可以对临界区代码进行排他性保护。

2.1.synchronized同步方法

synchronized同步方法是一种在Java中用于实现线程安全的机制。它提供了一种简单而强大的方式来确保多个线程对共享资源的互斥访问。当使用synchronized关键字来修饰一个方式的时候,该方法被声明为一个同步方法。使用方式也很简单

// 声明为同步方法
private synchronized void incrementing() {
    for (int j = 0; j < 10000; j++) {
        count++;
    }
}

再次运行程序

@Test
@DisplayName("测试synchronized同步方法")
public void test2() {
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 10; i++) {
        executorService.submit(() -> {
            incrementing();
        });
    }
    //  休眠10s
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    executorService.shutdown();
    log.error("预期值:100000");
    log.error("最终结果:{}", count);

}

private synchronized void incrementing() {
    for (int j = 0; j < 10000; j++) {
        count++;
    }
}

在这里插入图片描述

synchronized同步方法基于Java中的内置锁(也称为监视器锁)实现了线程的互斥访问。每个Java对象都与一个内置锁相关联。当一个线程进入synchronized同步方法时,它会自动获取该对象的内置锁。其他线程必须等待该锁的释放才能进入同步方法。

这样看似解决了问题,但是我们还要从并发性能角度来考虑,在synchronized同步方法中,默认情况下,锁的粒度是对象级别的。这意味着当一个线程执行同步方法时,其他线程无法同时执行该对象的同步方法。这种粒度适用于需要保护整个对象状态的场景,但可能导致较大的锁竞争和阻塞。

下面我们来了解一下synchronized同步代码块

2.2.synchronized同步代码块

对于较大的临界区代码段,为了保证执行的一个效率,我们最好将同步方法设置为小的临界区。

synchronized同步代码块是使用synchronized关键字来标识的一段代码,用于保护临界区,确保在同一时间只有一个线程可以执行该代码块。与synchronized同步方法不同,同步代码块只锁定指定的对象,而不是整个方法或对象。

通过仅在必要的代码段中使用synchronized同步代码块,可以减小同步区域的范围。这样可以提高并发性,因为其他线程不需要等待整个方法的执行完成,而只需要等待同步代码块执行完成。

// 主要此时锁的是是一个对象(obj),而不是一整个方法了
// 每当线程进入临界区代码段时,需要获取Obj对象的监视锁。因为每个对象都有一把监视锁,因为任何Java对象都可以作为synchronized的同步锁
synchronized(obj){
    // 临界区代码保护段
    ........
}

下面我们通过一个案例来了解下synchronized同步代码块

private int count = 0;

private Object lock = new Object();

@Test
@DisplayName("测试synchronized同步代码块")
public void test3() {
    long l = System.currentTimeMillis();
    executeTask();
    log.error("预期值:100000000");
    log.error("最终结果:{}", count);
    log.error("执行花费时间:{} ms", System.currentTimeMillis() - l);
}

private void executeTask() {
    // 创建一个线程集合
    ArrayList<Thread> threads = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        threads.add(new Thread(() -> {
            add();
        }));
    }
    for (Thread thread : threads) {
        thread.start();
    }

    threads.forEach(it -> {
        // 等待其他线程全部执行完毕
        try {
            it.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
}

private void add() {
    // 将锁的粒度控制再最小范围内
    synchronized (lock) {
        for (int j = 0; j < 10000000; j++) {
            count++;
        }
    }
}

在这里插入图片描述

这段代码中的synchronized同步代码块,它被用来保证对count变量的并发访问的线程安全。

  1. executeTask()方法中创建了10个线程,并启动它们执行add()方法。
  2. 每个线程在add()方法中,通过synchronized同步代码块对count变量进行10000000次的增加操作。
  3. synchronized同步代码块的锁对象是lock,多个线程在执行时会竞争这个锁对象。
  4. 当一个线程获取到锁对象后,其他线程需要等待锁的释放才能继续执行。
  5. 由于synchronized同步代码块的存在,每个线程在执行增加操作时都会获得锁,这样可以保证对count变量的操作是互斥的,避免了并发访问导致的数据不一致性问题。
  6. 最终,所有线程执行完毕后,测试方法输出了预期值和最终结果的日志信息,以及执行花费时间的日志信息。

根据代码的逻辑,预期结果应该是count的最终值为 100000000,因为每个线程都对count进行了 10000000 次的增加操作。

需要注意的是,由于synchronized同步代码块的锁粒度被控制在最小范围内,每个线程只在增加操作时获取锁,因此可以提高并发性能,减少了线程之间的竞争。这样可以确保线程之间的并发执行,同时保证了对count变量的线程安全访问。

2.3.synchronized同步方法和synchronized同步代码块区别

synchronized同步方法synchronized同步代码块有什么联系呢?其实在Java内部实现上,synchroinzed方法 等同于用一个synchronized代码块,只不过这个代码块包含了同步方法中的所有语句。然后再synchroinzed代码块中传入的是 this关键字

下面这个两种同步方式 通过JVM内部字节码编译后其实是 一致的

private void add() {
    // 将锁的粒度控制再最小范围内
    synchronized (this) {
        for (int j = 0; j < 10000000; j++) {
            count++;
        }
    }
}

private synchronized void add() {
    // 将锁的粒度控制再最小范围内
    for (int j = 0; j < 10000000; j++) {
        count++;
    }
}

2.4.静态的同步方法

在Java中有两种对象:Object实例对象和Class对象,每个类运行的时的类型用Class对象表示,它包含类名称,继承关系,字段,和方法相关信息。

JVM将类载入自己的方法区内存时,会为其创建一个Class对象,对于一个类来说Class对象是唯一的

Class对象没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机调用类加载器中的defineClass方法自动构造的,因为不能显式的声明一个Class对象

下面是一个synchronized关键字修饰static静态方法的例子

private synchronized static void add() {
    for (int j = 0; j < 10000000; j++) {
        count++;
    }
}

静态同步方法的特点:

  1. 锁对象:静态同步方法的锁对象是该方法所属的类对象,即类的Class对象。每个类在Java虚拟机中都有唯一的Class对象,它在类加载过程中被创建。因此,不同的类拥有不同的Class对象,不同的类对象之间的锁是互相独立的。
    1. 静态方法属于Class实例,而不是单个Object实例,而且静态方法内部也不可以访问Object实例的this引用(也称为指针,或者说句柄),所有当使用synchroinzed关键字修饰静态方法时,我们是没法获取的Object实例的this对象监视锁的。
  2. 锁范围:静态同步方法的锁范围是整个方法体。当一个线程进入静态同步方法时,它会尝试获取该类对象上的锁,其他线程需要等待锁的释放才能执行该方法。
    1. 使用synchronized关键字修饰的静态方法时,一个JVM所有争用线程 共同去竞争同一把锁,这个锁的粒度时非常粗的。
    2. 如果使用对象锁,那么JVM所有争用对象,竞争的是不同的对象锁,争用线程可以同步进入临界区,所得粒度是很细的。**
  3. 影响范围:静态同步方法影响的是该类的所有实例对象。当一个线程获得了静态同步方法的锁时,其他线程无法同时访问该类的其他静态同步方法,但可以同时访问该类的非静态同步方法或非同步方法。这是因为静态同步方法使用的是类对象作为锁对象,而实例方法使用的是实例对象作为锁对象。
  4. 类级别同步:静态同步方法实际上是在类级别上进行同步。通过静态同步方法,可以保证多个线程对该类的静态变量的并发访问是线程安全的。这对于需要保护类级别共享资源的场景非常有用。
  5. 静态同步方法与实例方法的区别:静态同步方法和实例方法之间的同步是互相独立的。静态同步方法使用的是类对象作为锁对象,而实例方法使用的是实例对象作为锁对象。因此,静态同步方法和实例方法可以在多线程环境下同时执行,它们之间的锁不会互相影响。

2.5.内置锁的释放

通过synchronized抢占的锁,什么时候释放呢?

当一个线程通过synchronized关键字抢占到锁时,它会执行同步代码块或同步方法中的代码。锁会在以下几种情况下释放:

  1. 正常执行完成:当线程执行完同步代码块或同步方法中的所有语句,即执行到代码块的末尾或方法的返回处,锁会自动释放。这样,其他线程就有机会获得锁并执行相应的同步代码块或同步方法。
  2. 异常情况:如果在同步代码块或同步方法中抛出了未被捕获的异常,锁也会被释放。异常会终止当前线程的执行,因此在异常发生时,JVM会确保锁的释放,以允许其他线程继续执行。
  3. 调用wait()方法:当线程在同步代码块或同步方法中调用了对象的wait()方法,它会释放锁并进入等待状态。在等待期间,该线程会让出锁给其他线程,直到被其他线程通过notify()notifyAll()方法唤醒后,才会重新竞争锁。
  4. 调用notify()notifyAll()方法:当线程在同步代码块或同步方法中调用了对象的notify()notifyAll()方法,它会唤醒等待在该对象上的一个或多个线程。被唤醒的线程会重新竞争锁,一旦获得锁,就可以继续执行同步代码块或同步方法。

需要注意的是,锁的释放是自动进行的,程序员不需要显式地释放锁。当锁被释放后,其他线程可以竞争获取锁并执行相应的同步代码块或同步方法。这种锁的释放机制保证了多个线程之间的互斥访问,从而实现了同步和线程安全。

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

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

相关文章

vue2 webpack-dev-server Unknown promise rejection reason

在vue.config.js中添加如下配置&#xff0c;重启项目即可 module.exports defineConfig({devServer: {client: {overlay: false,},} })参考

2024最新CTF入门的正确路线

目录 前言 一、什么是CTF比赛&#xff1f; 二、CTF比赛的流程 三、需要具备的知识 四、总结 前言 随着网络安全意识的增强&#xff0c;越来越多的人开始涉足网络安全领域&#xff0c;其中CTF比赛成为了重要的学习和竞赛平台。本人从事网络安全工作多年&#xff0c;也参加过…

【智能优化算法】海象优化器(Walrus optimizer,WO)

海象优化器(Walrus optimizer&#xff0c;WO)是期刊“EXPERT SYSTEMS WITH APPLICATIONS”&#xff08;中科院一区 IF 8.3&#xff09;的2024年智能优化算法 01.引言 海象优化器(Walrus optimizer&#xff0c;WO)的灵感来自海象通过接收关键信号(危险信号和安全信号)选择迁徙、…

网络基础(1)详解

目录 1.计算机网络背景 2.网络协议 3.网络中的地址管理 1.计算机网络背景 1.1 网络发展 (1)计算机从独立模式到网络互联(多态计算机连接共享数据)再到局域网LAN(通过交换机和路由器连接)接着是广域网WAN 1.2 协议 协议就是双方的一种约定. 为什么要有协议? 因为在数据长距…

练习项目后端代码解析切面篇(Aspect)

前言 之前注解篇时我说&#xff0c;通常情况下一个自定义注解一般对应一个切面&#xff0c;虽然项目里的切面和注解个数相同&#xff0c;但是好像有一个名字看起来并不对应&#xff0c;无所谓&#xff0c;先看了再说。 ExceptionLogAspect切面 我在里面做了具体注释&#x…

Java 线程池 ( Thread Pool )的简单介绍

想象一下&#xff0c;你正指挥着一支超级英雄团队&#xff0c;面对蜂拥而至的敌人&#xff08;任务&#xff09;&#xff0c;不是每次都召唤新英雄&#xff08;创建线程&#xff09;&#xff0c;而是精心调配现有成员&#xff0c;高效应对。这就是Java线程池的魔力&#xff0c;…

口感与风味的完善结合:精酿啤酒的多样风格

啤酒的世界是丰富多彩的&#xff0c;不同的啤酒有着各自与众不同的口感和风味。而Fendi club啤酒&#xff0c;作为精酿啤酒的代表&#xff0c;以其多样化的风格和卓着的口感&#xff0c;吸引了无数啤酒爱好者的目光。 Fendi club啤酒的多样风格&#xff0c;首先体现在其原料的选…

【教学类-50-14】20240505“数一数”图片样式12:数一数(12个“人物”图案)

作品展示 背景需求&#xff1a; 前文做了“”材料”图片的数一数学具&#xff0c;效果不错&#xff0c; https://blog.csdn.net/reasonsummer/article/details/138466325https://blog.csdn.net/reasonsummer/article/details/138466325 为了让图案内容更丰富&#xff0c;我又…

2024-05-07 商业分析-如何在社会层面做一个更好的工具人-记录

摘要: 2024-05-07 商业分析-如何成为一个靠谱的工具人 如何在社会层面做一个更好的工具人 那么今天讲的这个主题呢&#xff0c;对吧&#xff1f;你们一看啊&#xff0c;就觉得这个就不应该我讲是吧啊&#xff0c;但是呢这个逻辑呢我还得跟你们讲一下啊&#xff0c;就是如何成为…

java面向对象实现文字格斗游戏

面向对象编程&#xff08;Object-Oriented Programming, OOP&#xff09;是一种程序设计思想&#xff0c;它利用“对象”来封装状态和行为&#xff0c;使得代码更易于维护和扩展。 下面我们使用java中的面向对象编程&#xff0c;来实现一个文字格斗的游戏联系&#xff01; 实…

Day21 代码随想录打卡|字符串篇---右旋转字符串

题目&#xff08;卡码网 T55&#xff09;&#xff1a; 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&#xff0c;将字符串中的后面 k 个字符移到字符串的前面&#xff0c;实现字符串的右旋转…

京东生产环境十万并发秒杀系统三高架构

文章目录 三高——高并发、高可用、高可扩展用数据库乐观锁解决超卖阿里巴巴&#xff1a;为了提升数据库性能&#xff0c;对数据库的源码级别做了改造——在DB内部实现内存队列&#xff0c;一次性接收很多的请求&#xff0c;一次性更新。京东&#xff1a;redis&#xff0c;mq&a…

微软开发新模型;YouTube 推出新AI功能;可折叠iPhone 或发布?

微软或开发新模型与 Google、OpenAI 竞争 The Information 报道&#xff0c;微软正在训练一种新的 AI 大模型「MAI-1」&#xff0c;规模上足以与 Google、Anthropic 乃至 OpenAI 的先进模型抗衡。 据报道&#xff0c;这个 MAI-1 模型由微软聘请的 Inflection 前 CEO Mustafa S…

【Linux】Linux线程

一、Linux线程的概念 1.什么是线程 1.一个进程的一个执行线路叫做线程&#xff0c;线程的一个进程内部的控制序列。 2.一个进程至少有一个执行线程 3.线程在进程内部&#xff0c;本质是在进程地址空间内运行 4.操作系统将进程虚拟地址空间的资源分配给每个执行流&#xff0…

每日OJ题_DFS解决FloodFill⑤_力扣417. 太平洋大西洋水流问题

目录 力扣417. 太平洋大西洋水流问题 解析代码 力扣417. 太平洋大西洋水流问题 417. 太平洋大西洋水流问题 难度 中等 有一个 m n 的矩形岛屿&#xff0c;与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界&#xff0c;而 “大西洋” 处于大陆的右边界和下…

题目----力扣--移除链表元素

题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&…

2024年4月17日华为春招实习试题【三题】-题目+题解+在线评测,2024.4.17,华为机试

2024年4月17日华为春招实习试题【三题】-题目题解在线评测 &#x1f52e;题目一描述&#xff1a;扑克牌消消乐输入描述输出描述样例一样例二Limitation解题思路一&#xff1a;模拟&#xff0c;遇到连续3张相同牌号的卡牌&#xff0c;直接删除解题思路二&#xff1a;栈解题思路三…

【备战软考(嵌入式系统设计师)】08 - 多媒体技术信息安全

多媒体技术 这内容比较杂&#xff0c;而且跟咱嵌入式的关系不大&#xff0c;但是软考里会考一些&#xff0c;下面我就结合我已经刷过的一千多道往年真题概括总结一下常考的知识点。 媒体分类 首先媒体分为五类&#xff1a; 感觉媒体&#xff0c;让人直接感觉得到的媒体&…

热敏电阻怎么进行性能测试?并以LabVIEW为例进行说明

过程也可用于执行热敏电阻测量。RTD和热敏电阻遵循非常相似的功能原理&#xff0c;测量步骤与下面提供的步骤相同。有关热敏电阻的更多信息&#xff0c;请参阅本文档。 查找设备引脚排列 在连接任何信号之前&#xff0c;请找到您的设备引脚排列。 打开NI MAX并展开设备和接口。…

【Java笔记】多线程:一些有关中断的理解

文章目录 线程中断的作用线程的等待状态WAITINGTIMED_WAITING 线程从等待中恢复 java.lang.Thread中断实现相关方法中断标识interrupted 一些小练习Thread.interrupt() 只唤醒线程并修改中断标识sleep() 清除中断状态标识 Reference 线程中断的作用 线程中断可以使一个线程从等…