2024年java面试--多线程(4)

news2024/12/23 5:16:31

系列文章目录

  1. 2024年java面试(一)–spring篇
  2. 2024年java面试(二)–spring篇
  3. 2024年java面试(三)–spring篇
  4. 2024年java面试(四)–spring篇
  5. 2024年java面试–集合篇
  6. 2024年java面试–redis(1)
  7. 2024年java面试–redis(2)

文章目录

  • 系列文章目录
  • 内存模型
    • 1、volatile底层实现
    • 2、AQS思想
    • 3、happens-before
  • 启动线程方法 start()和 run()有什么区别?
  • 多线程同步有哪几种方法?
  • 死锁
  • 多线程之间如何进行通信
  • 线程怎样拿到返回结果
  • 多线程执行问题
    • Q1:有 A、B、C 三个线程,如何保证三个线程同时执行?
    • Q2:有 A、B、C 三个线程,在并发情况下,如何保证三个线程依次执行?
    • Q3:有 A、B、C 三个线程,如何保证三个线程有序交错执行?
  • 线程池启动线程 submit()和 execute()方法有什么不同
  • 活锁、饥饿、无锁、死锁
  • 什么是原子性、可见性、有序性
  • 什么是守护线程?有什么用?
  • 如何创建线程安全的单例模式
  • 如果你提交任务时,线程池队列已满,这时会发生什么
  • Synchrpnized和lock的区别
  • 常见线程安全的并发容器有哪些


内存模型

Java 内存模型(Java Memory Model,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了 Java 程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
在这里插入图片描述
JMM 是一种规范,是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。目的是保证并发编程场景中的原子性、可见性和有序性。

原子性

在 Java 中,为了保证原子性,提供了两个高级的字节码指令 Monitorenter 和 Monitorexit。这两个字节码,在 Java 中对应的关键字就是 Synchronized。因此,在 Java 中可以使用 Synchronized 来保证方法和代码块内的操作是原子性的。

可见性

Java 中的 Volatile 关键字修饰的变量在被修改后可以立即同步到主内存。被其修饰的变量在每次使用之前都从主内存刷新。因此,可以使用 Volatile 来保证多线程操作时变量的可见性。除了 Volatile,Java 中的 Synchronized 和 Final 两个关键字也可以实现可见性。只不过实现方式不同

有序性

在 Java 中,可以使用 Synchronized 和 Volatile 来保证多线程之间操作的有序性。区别:Volatile 禁止指令重排。Synchronized 保证同一时刻只允许一条线程操作。

1、volatile底层实现

作用:

1.可见性: 当一个线程修改了volatile修饰的变量的值,其他线程可以立即看到这个修改,保证了共享变量的可见性。

2.禁止指令重排序: 编译器和处理器在编译和执行代码时,可能会对指令进行重排序,但是volatile关键字可以禁止这种重排序,保证了程序的正确性。

3.保证原子性: volatile关键字可以保证一些简单的操作的原子性,例如++操作,但是对于复合操作,volatile关键字无法保证原子性。

底层实现:

“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
  
  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;
  
  2)它会强制将对缓存的修改操作立即写入主存;
  
  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
  
单例模式中volatile的作用:(DCL(Double Check Lock)单例为什么要加Volatile?)

防止代码读取到instance不为null时,instance引用的对象有可能还没有完成初始化。

volatile防止指令重排,在DCL中,防止高并发情况下,指令重排造成的线程安全问题。

class Singleton{
    private volatile static Singleton instance = null;   //禁止指令重排
    private Singleton() {
         
    }
    public static Singleton getInstance() {
        if(instance==null) { //减少加锁的损耗
            synchronized (Singleton.class) {
                if(instance==null) //确认是否初始化完成
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

volatile关键字和synchronized关键字的区别

  1. volatile关键字保证了共享变量的可见性和禁止指令重排序,但是无法保证原子性,而synchronized关键字可以保证原子性、有序性和可见性。
  2. volatile关键字适用于一些简单的操作,例如++操作,而synchronized关键字适用于复合操作。
  3. volatile关键字不会造成线程阻塞,而synchronized关键字可能会造成线程阻塞。

volatile能不能保证线程安全?

volatile不能保证线程安全,只能保证线程可见性,不能保证原子性

flag常量未使用volatile关键字的时候,程序一直不停止,因为无法感知flag的变化

使用volatile关键字后,因为volatile保证了可见性,感知到了flag的变化,程序停止并打印
在这里插入图片描述

2、AQS思想

AQS的全称为(AbstractQueuedSynchronizer)抽象的队列式的同步器,是⼀个⽤来构建锁和同步器的框架,使⽤AQS能简单且⾼效地构造出应⽤⼴泛的⼤量的同步器,如:基于AQS实现的lock, CountDownLatch、CyclicBarrier、Semaphore需解决的问题:

  • 状态的原子性管理
  • 线程的阻塞与解除阻塞
  • 队列的管理

AQS核⼼思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的⼯作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占⽤,那么就需要⼀套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是⽤CLH(虚拟的双向队列) 队列锁实现的,即将暂时获取不到锁的线程加⼊到队列中。

lock:

是一种可重入锁,除了能完成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。默认为非公平锁,但可以初始化为公平锁; 通过方法 lock()与 unlock()来进行加锁与解锁操作;

常见AQS锁:

CountDownLatch(倒计数器):

通过计数法(倒计时器),让一些线程堵塞直到另一个线程完成一系列操作后才被唤醒;该⼯具通常⽤来控制线程等待,它可以让某⼀个线程等待直到倒计时结束,再开始执⾏。具体可以使用countDownLatch.await()来等待结果。多用于多线程信息汇总。

CompletableFuture(独占锁):

通过设置参数,可以完成CountDownLatch同样的多平台响应问题,但是可以针对其中部分返回结果做更加灵活的展示。

CyclicBarrier(循环栅栏):

字面意思是可循环(Cyclic)使用的屏障(Barrier)。他要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。可以用于批量发送消息队列信息、异步限流。

Semaphore(信号量):

信号量主要用于两个目的,一个是用于多个共享资源的互斥作用,另一个用于并发线程数的控制。SpringHystrix限流的思想

为什么AQS使用的双向链表

因为有一些线程可能发生中断 ,而发生中断时候就需要在同步阻塞队列中删除掉,这个时候用双向链表方便删除掉中间的节点

3、happens-before

用来描述和可见性相关问题:如果第一个操作 happens-before 第二个操作,那么我们就说第一个操作对于第二个操作是可见的

常见的happens-before:volatile 、锁、线程生命周期。


启动线程方法 start()和 run()有什么区别?

只有调用了 start()方法,才会表现出多线程的特性,不同线程的 run()方法里面的代码交替执行。如果只是调用 run()方法,那么代码还是同步执行的,必须等待一个线程的 run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其 run()方法里面的代码。


多线程同步有哪几种方法?

Synchronized 关键字,Lock 锁实现,分布式锁等。


死锁

死锁就是两个线程相互等待对方释放对象锁


多线程之间如何进行通信

wait/notify


线程怎样拿到返回结果

实现Callable 接口


多线程执行问题

Q1:有 A、B、C 三个线程,如何保证三个线程同时执行?

保证线程同时执行可以用于并发测试。可以使用倒计时锁CountDownLatch实现让三个线程同时执行。

将其计数器初始为1,多个线程在开始执行任务前首先countdownlatch.await(),当主线程调用countDown()方法时,计数器变为0,多个线程同时被唤醒执行任务。

代码如下所示:

        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(1);
​
        executorService.submit(()->{
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }System.out.println("线程A执行,执行时间:" + System.currentTimeMillis());
        });
​
        executorService.submit(()->{
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }System.out.println("线程B执行,执行时间:" + System.currentTimeMillis());
        });
​
        executorService.submit(()->{
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }System.out.println("线程C执行,执行时间:" + System.currentTimeMillis());
        });
​
        countDownLatch.countDown();

打印内容如下,通过时间可以证明三个线程是同时执行的。

线程A执行,执行时间:1617811258309
线程C执行,执行时间:1617811258309
线程B执行,执行时间:1617811258309

让三个线程同时执行,也可以使用栅栏 CyvlivBarrier 来实现,当三个线程都到达栅栏处,才开始执行。

Q2:有 A、B、C 三个线程,在并发情况下,如何保证三个线程依次执行?

  1. 用 join 方法

使用 join() 方法可以保证线程的顺序执行。在 Java 中,join() 方法是用来等待一个线程执行完成的方法,当调用某个线程的 join() 方法时,当前线程会被阻塞,直到该线程执行完成后才会继续执行。

具体来说,我们可以在 T1 线程结束时调用 T2 的 join() 方法,这样 T2 就会等待 T1 执行完成后再开始执行;同理,在 T2 结束时调用 T3 的 join() 方法,以确保 T3 在 T2 执行完成后才开始执行。这样就可以保证 T1、T2、T3 按照顺序依次执行。

  1. 使用CountDownLatch(闭锁)

使用 CountDownLatch(闭锁)方法可以保证线程的顺序执行。CountDownLatch 是一个同步工具类,它可以让某个线程等待多个线程完成各自的工作之后再继续执行。

@Test
    public void testUseCountDownLatch() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch aLatch = new CountDownLatch(1);
        CountDownLatch bLatch = new CountDownLatch(1);
        CountDownLatch cLatch = new CountDownLatch(1);
​
        executorService.submit(() -> {
            try {
                aLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }for (int i = 0; i < 10; i++) {
                System.out.println("A - " + i);
            }
​
            bLatch.countDown();
        });
​
        executorService.submit(() -> {
            try {
                bLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }for (int i = 0; i < 10; i++) {
                System.out.println("B - " + i);
            }
​
            cLatch.countDown();
        });
​
        executorService.submit(() -> {
            try {
                cLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }for (int i = 0; i < 10; i++) {
                System.out.println("C - " + i);
            }
        });
​
        aLatch.countDown();
    }

  1. volatile 使用一个变量进行判断执行哪个线程。没有轮到的线程在不停循环,没有停止线程
private volatile int count = 0;/**
 * 使用一个变量进行判断执行哪个线程。没有轮到的线程在不停循环,没有停止线程
 */
public void useVolatile() {ExecutorService executorService = Executors.newCachedThreadPool();
​
    executorService.submit(() -> {
        while (true) {
            if (count == 0) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("A - " + i);
                }
                count = 1;
                break;
            }
        }
    });
​
    executorService.submit(() -> {
        while (true) {
            if (count == 1) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("B - " + i);
                }
                count = 2;
                break;
            }
        }
    });
​
    executorService.submit(() -> {
        while (true) {
            if (count == 2) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("C - " + i);
                }
                count = 3;
                break;
            }
        }
    });
}

  1. 使用单个线程池

使用单个线程池可以保证t1、t2、t3顺序执行,因为单个线程池只有一个工作线程,每次只会执行一个任务。我们可以将t1、t2、t3三个任务按照顺序提交给单个线程池,这样就可以确保它们按照顺序依次执行。

Q3:有 A、B、C 三个线程,如何保证三个线程有序交错执行?

实现三个线程交错打印,可以使用ReentrantLock以及3个Condition来实现


线程池启动线程 submit()和 execute()方法有什么不同

execute没有返回值,如果不需要知道线程的结果就使用execute方法,性能会好很多。

submit返回一个Future对象,如果想知道线程结果就使用submit提交,而且它能在主线程中通过Future的get方法捕获线程中的异常。


活锁、饥饿、无锁、死锁

死锁

死锁是多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又相互等对方释放锁,此时若无外力干预,这些线程则一直处理阻塞的假死状态,形成死锁。举个例子,A同学抢了B同学的钢笔,B同学抢了A同学的书,两个人都相互占用对方的东西,都在让对方还给自己自己再还,这样一直争执下去等待对方还而又得不到解决,老师知道此事后就让他们相互还给对方,这样在外力的干预下他们才解决,当然这只是个例子没有老师他们也能很好解决,计算机不像人如果发现这种情况没有外力干预还是会一直阻塞下去的。

活锁

活锁这个概念大家应该很少有人听说或理解它的概念,而在多线程中这确实存在。活锁恰恰与死锁相反,死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到资源却又相互释放不执行。当多线程中出现了相互谦让,都主动将资源释放给别的线程使用,这样这个资源在多个线程之间跳动而又得不到执行,这就是活锁。

饥饿

我们知道多线程执行中有线程优先级这个东西,优先级高的线程能够插队并优先执行,这样如果优先级高的线程一直抢占优先级低线程的资源,导致低优先级线程无法得到执行,这就是饥饿。当然还有一种饥饿的情况,一个线程一直占着一个资源不放而导致其他线程得不到执行,与死锁不同的是饥饿在以后一段时间内还是能够得到执行的,如那个占用资源的线程结束了并释放了资源。

无锁

无锁,即没有对资源进行锁定,即所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。无锁典型的特点就是一个修改操作在一个循环内进行,线程会不断的尝试修改共享资源,如果没有冲突就修改成功并退出否则就会继续下一次循环尝试。所以,如果多个线程修改同一个值必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。之前的文章我介绍过JDK的CAS原理及应用即是无锁的实现。


什么是原子性、可见性、有序性

原子性

原子性是指一个线程的操作是不能被其他线程打断,同一时间只有一个线程对一个变量进行操作。在多线程情况下,每个线程的执行结果不受其他线程的干扰,比如说多个线程同时对同一个共享成员变量n++100次,如果n初始值为0,n最后的值应该是100,所以说它们是互不干扰的,这就是传说的中的原子性。但n++并不是原子性的操作,要使用AtomicInteger保证原子性。

可见性

可见性是指某个线程修改了某一个共享变量的值,而其他线程是否可以看见该共享变量修改后的值。在单线程中肯定不会有这种问题,单线程读到的肯定都是最新的值,而在多线程编程中就不一定了。每个线程都有自己的工作内存,线程先把共享变量的值从主内存读到工作内存,形成一个副本,当计算完后再把副本的值刷回主内存,从读取到最后刷回主内存这是一个过程,当还没刷回主内存的时候这时候对其他线程是不可见的,所以其他线程从主内存读到的值是修改之前的旧值。像CPU的缓存优化、硬件优化、指令重排及对JVM编译器的优化,都会出现可见性的问题。

有序性

我们都知道程序是按代码顺序执行的,对于单线程来说确实是如此,但在多线程情况下就不是如此了。为了优化程序执行和提高CPU的处理性能,JVM和操作系统都会对指令进行重排,也就说前面的代码并不一定都会在后面的代码前面执行,即后面的代码可能会插到前面的代码之前执行,只要不影响当前线程的执行结果。所以,指令重排只会保证当前线程执行结果一致,但指令重排后势必会影响多线程的执行结果。虽然重排序优化了性能,但也是会遵守一些规则的,并不能随便乱排序,只是重排序会影响多线程执行的结果。


什么是守护线程?有什么用?

什么是守护线程?与守护线程相对应的就是用户线程,守护线程就是守护用户线程,当用户线程全部执行完结束之后,守护线程才会跟着结束。也就是守护线程必 须伴随着用户线程,如果一个应用内只存在一个守护线程,没有用户线程,守护线程自然会退出。

举例,GC垃圾回收线程:就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是VM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。


如何创建线程安全的单例模式

  1. 饿汉式:线程安全速度快,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了。
  2. 懒汉式:双重检测锁,第一次减少锁的开销、第二次防止重复、volatile防止重排序导致实例化未完成,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

如果你提交任务时,线程池队列已满,这时会发生什么

  1. 如果使用的无界队列,那么可以继续提交任务时没关系的

  2. 如果使用的有界队列,提交任务时,如果队列满了,如果核心线程数没有达到上限,那么则增加线程,如果线程数已经达到了最大值,则使用拒绝策略进行拒绝


Synchrpnized和lock的区别

  • synchronized是关键字,lock是一个类

  • synchronized在发生异常时会自动释放锁,lock需要手动释放锁

  • synchronized是可重入锁、非公平锁、不可中断锁,lock的ReentrantLock是可重入锁,可中断锁,可以是公平锁也可以是非公平锁

  • synchronized是JVM层次通过监视器实现的,Lock是通过AQS实现的


常见线程安全的并发容器有哪些

  1. CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap
  2. CopyOnWriteArrayList、CopyOnWriteArraySet采用写时复制实现线程安全
  3. ConcurrentHashMap采用分段锁的方式实现线程安全

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

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

相关文章

BCSP-玄子Share-Java框基础_双系统Redis安装与基础语法

四、Redis 4.1 Redis 简介 Redis 是开源、高性能的key-value数据库&#xff0c;属于 NoSQL 数据库 NoSQL 数据库与关系型数据库 关系型数据库&#xff1a;采用关系模型来组织数据&#xff0c;主要用于存储格式化的数据结构NoSQL 数据库&#xff1a;泛指非关系型数据库&…

力扣|找出和所对应的两数的下标

从零开始刷力扣&#xff08;bushi 题目放在这&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值target的两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一…

数电发票开票接口

全电发票&#xff0c;也叫数电发票&#xff0c;全称为全面数字化的电子发票。数电发票是依托可信身份体系和电子发票服务平台&#xff1b;以去介质、去版式、标签化、要素化、授信制、赋码制为基本特征&#xff1b;覆盖全领域、全环节、全要素的全新发票。与纸质发票具有同等法…

tkinter控件样式

文章目录 以按钮为例共有参数动态属性 tkinter系列&#xff1a; GUI初步&#x1f48e;布局&#x1f48e;绑定变量&#x1f48e;绑定事件&#x1f48e;消息框&#x1f48e;文件对话框&#x1f48e;控件样式扫雷小游戏&#x1f48e;强行表白神器 以按钮为例 tkinter对控件的诸…

05-Redis

1、Redis为什么快&#xff1f; 1、纯内存操作 2、单线程可以省去多线程时CPU上下文会切换的时间 3、渐进式ReHash、缓存时间戳 数组需要扩容的时候&#xff0c;他会维护两张hash表&#xff0c;比如第一张的数组长度为6&#xff0c;另一张的数组长度为12&#xff0c;在set和g…

JavaScript中的事件循环(event loop)机制

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 调用栈&#xff08;Call Stack&#xff09;⭐ 消息队列&#xff08;Message Queue&#xff09;⭐ 事件循环&#xff08;Event Loop&#xff09;⭐ 宏任务和微任务⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇…

如何取消KEIL-MDK工程中出现的CMSIS绿色图标

如何取消KEIL-MDK工程中出现的CMSIS绿色图标&#xff1f;我以前经常遇到&#xff0c;不知道怎么搞&#xff0c;好像也不影响编译结果。以前问过其他人&#xff0c;但是不知道怎么搞&#xff0c;相信很多人也遇到过。水平有限&#xff0c;表达不清楚&#xff0c;见下图&#xff…

Java 反射调用自己写的对象方法

一、java 反射的定义 定义&#xff1a;JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意一个方法&#xff0c;这种动态获取、调用对象方法的功能称为java语言的…

JavaScript 中的原型到底该如何理解?

JavaScript作为一个基于原型的OOP&#xff0c;和我们熟知的基于类的面向对象编程语言有很大的差异。如果不理解其中的本质含义&#xff0c;则无法深入理解JavaScript的诸多特性&#xff0c;以及由此产生的诸多“坑”。在讨论“原型”的概念之前&#xff0c;我们先来讨论一下“类…

APS系统设计经验分享(时间推导II - 2023.09)

在前一篇关于APS系统设计分享文章(《APS系统设计经验分享(时间推导 - 2023.03)》)中&#xff0c;我们提到将会分享使用OptaPlanner作为规划引擎开发APS系统过程中&#xff0c;遇到的一些时间相关的设计建议与异常情况分析。后来一直忙于项目工作&#xff0c;直到现在才想起仍欠…

CSS---flex布局

主要记录flex布局的要点以及实例 flex flex父标签的6个属性flex-direction: flex布局的方向flex-wrap: 是否可以换行flex-flow: flex-direction 和 flex-wrap 一起写justify-content&#xff1a;横向对齐方式align-items: 纵向对齐方式align-content: 有换行情况下的纵向对齐方…

DR IP-SoC China 2023 Day演讲预告 | 龙智Perforce专家解析芯片开发中的数字资产管理

2023年9月6日&#xff08;周三&#xff09;&#xff0c;龙智即将亮相于上海举行的D&R IP-SoC China 2023 Day&#xff0c;呈现集成了Perforce与Atlassian产品的芯片开发解决方案&#xff0c;助力企业更好、更快地进行芯片开发。 D&R IP-SoC China 2023 Day 是中国首个…

[论文笔记]ESIM

引言 这是经典论文Enhanced LSTM for Natural Language Inference的笔记。 本篇论文文是建立在自然语言推理(Natural Language Inference,NLI)任务上的。提出了简单的通过基于LSTM的序列推理模型效果到达了当时的SOTA水平。同时基于该模型,在局部推理建模层和推理组合层使用了…

三维模型OBJ格式轻量化压缩在大规模场景的加载和渲染的作用分析

三维模型OBJ格式轻量化压缩在大规模场景的加载和渲染的作用分析 OBJ格式是一种常用的三维模型文件格式&#xff0c;它存储了三维模型的几何信息和纹理坐标等相关属性。在大规模场景中加载和渲染三维模型时&#xff0c;OBJ格式的轻量化压缩对于提高性能和效率起到了重要的作用。…

Java多线程(Thread)详解之启动与中断

在我的前一篇博客中直接介绍了Thread的”五种“打开方式&#xff1a;Thread的”五种“打开方式https://blog.csdn.net/qq_45875349/article/details/132644717?spm1001.2014.3001.5501 但是还没有详细的对Thread类进行说明&#xff0c;这篇博客主要对Thread类进行介绍&#x…

软件产品选型测试POC测试怎么做?

软件poc测试 一、基本概述 软件选型测试是在软件采购的前提或采购过程中&#xff0c;通过对待选软件进行测试比对&#xff0c;筛选出适合的软件。通过对候选的软件进行量化或非量化的横向比对测试&#xff0c;为建设单位选择产品、供货方参加投标提供较直接的依据。 二、测试…

现在的校招面试,管你是不是应届生

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 一、他&#xff0c;上来打我&#xff01; 【字节】除了MQ解耦发奖&#xff0c;是否还有比MQ更优的解决方案&#xff1f;【字节】…

算法通关村16关 | 堆与滑动窗口问题结合

1. 堆与滑动窗口问题结合 题目 LeetCode239 给你一个整数数组nums&#xff0c;有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧&#xff0c;你可以看到在滑动窗口内的k个数字&#xff0c;滑动窗口每次只向右移动一位&#xff0c;返回滑动窗口中的最大值。 思路 对于…

css 文字单行多行超出长度后显示 ...

0.超出… 1、单行文本超出 <div class"content">测试数据&#xff1a;css单行文本超出显示省略号--------</div><style> .content{width: 200px;height: 200px;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;-o-text-overflow:el…

linux信号量

通过学习linux的信号量&#xff0c;对linux的信号量进行了编程。