【面试题】多线程

目录

  • 什么是线程?它与进程的区别是什么?
  • 解释一下并行与并发的区别。
  • 简述线程安全的概念,并举例说明。
  • 如何实现线程同步?有哪些常见的同步机制?
  • 在Java中,如何创建线程?谈谈继承Thread类与实现Runnable接口的区别。
  • Java中synchronized关键字的作用是什么?它有几种使用方式?
  • 解释一下volatile关键字的作用及其与synchronized的区别。
  • 什么是Java内存模型(JMM)?它如何影响多线程程序的执行?
  • 谈谈wait(), notify(), 和notifyAll()方法的使用场景及注意事项。
  • 线程池的七个参数
  • 线程安全的类和线程不安全的类分别都有哪些

什么是线程?它与进程的区别是什么?

线程是操作系统中最小的执行单位,它是进程中的一个执行流程。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件句柄等。

与进程相比,线程的主要区别如下:

  1. 资源占用:线程共享进程的资源,因此创建和切换线程的开销相对较小,而进程有自己独立的资源,创建和切换进程的开销较大。

  2. 调度和切换:线程是调度的基本单位,多个线程在同一进程中共享CPU的时间片,由操作系统进行线程调度。而进程是独立调度的基本单位,多个进程通过操作系统进行调度。

  3. 通信机制:线程可以通过共享内存的方式进行通信,由于共享内存的开销较小,线程间的通信相对较快。而进程之间的通信需要借助于进程间通信(IPC)机制,如管道、套接字等,通信开销相对较大。

  4. 独立性:线程是进程的一部分,多个线程共享进程的地址空间,共享相同的上下文,因此一个线程崩溃可能导致整个进程崩溃。而进程是相互独立的,一个进程崩溃不会影响其他进程的正常运行。

总而言之,线程是进程的一部分,是操作系统进行调度的基本单位,线程之间共享进程资源;而进程是相互独立的执行实体,具有独立的地址空间和资源。

解释一下并行与并发的区别。

并行和并发是两个与多任务处理相关的概念,它们描述的是不同的任务执行方式。

  • 并发:指的是系统中同时存在多个任务,在同一时间段内,交替执行这些任务。这些任务之间可能是交错执行的,看起来是同时进行的。并发可以通过时间片轮转或优先级调度等技术实现。

  • 并行:指的是系统中同时执行多个任务,并且这些任务真正地同时进行,通过多个处理单元或多核处理器并行执行。并行的任务是同时进行的,而不是交错执行。

简而言之,关键区别在于任务是否真正同时进行。并发是指多个任务交替执行,通过快速的切换使得多个任务看起来是同时进行;而并行是指多个任务真正同时进行,通过多个处理单元实现。

举个例子来说明,假设有两个任务A和B,如果采用并发方式执行,系统会轮流执行A和B,并在它们之间进行快速切换;如果采用并行方式执行,系统会用两个处理单元分别同时执行A和B。

并发常用于单核处理器,通过时间片轮转使得多个任务交替执行,提高系统资源利用率;而并行常用于多核处理器或多个处理单元,通过同时执行多个任务,加快任务完成速度。

简述线程安全的概念,并举例说明。

线程安全是指在多线程环境下,对共享数据的访问和操作能够得到正确的结果,并且不会导致数据的不一致性或意外的错误。

在多线程程序中,多个线程同时访问和操作共享的数据时,可能会出现以下问题:

  1. 竞态条件(Race Condition):多个线程竞争同一资源,导致结果的不确定性。
  2. 数据竞争(Data Race):多个线程同时读写共享数据,导致数据的不一致性。
  3. 死锁(Deadlock):多个线程相互等待对方释放资源导致程序无法继续执行。

为了确保线程安全,可以采取以下措施:

  1. 互斥锁(Mutex):使用锁机制保护共享资源,同一时间只允许一个线程访问。
  2. 原子操作(Atomic Operation):使用原子操作保证对共享数据的操作是不可分割的,不会被其他线程干扰。
  3. 条件变量(Condition Variable):使用条件变量进行线程间的同步,使得线程按照特定的条件等待或唤醒。

举个例子,假设有一个程序需要计数器来记录某个事件发生的次数。如果多个线程同时对计数器进行读写操作,就会存在数据竞争的问题。为了保证线程安全,可以使用互斥锁来保护计数器,确保同一时间只允许一个线程对其进行访问。这样可以避免竞态条件和数据竞争的问题,保证计数器的结果是正确的。

如何实现线程同步?有哪些常见的同步机制?

实现线程同步可以通过以下常见的同步机制来实现:

  1. 互斥锁(Mutex):互斥锁可以确保同一时间只有一个线程可以访问被保护的共享资源。当一个线程获取到互斥锁后,其他线程需要等待锁释放才能访问该资源。

  2. 信号量(Semaphore):信号量可以用来控制对共享资源的访问数量。它维护一个计数器,当计数器为0时,表示资源被占用,其他线程需要等待。当某个线程访问完资源后,会对计数器进行增加操作,表示资源已经释放。

  3. 条件变量(Condition Variable):条件变量用于线程间的同步和通信。它可以让线程等待某个特定条件发生,当条件满足时,可以被唤醒继续执行。

  4. 屏障(Barrier):屏障用于等待一组线程达到某个共同的点后再继续执行。当所有线程都达到屏障点时,屏障会打开,允许线程继续执行。

  5. 原子操作(Atomic Operation):原子操作是一种不可分割的操作,能够保证对共享数据的操作是线程安全的。原子操作的执行过程中不能被其他线程中断。

  6. 读写锁(Read-write Lock):读写锁允许多个线程同时读取共享资源,但在写操作时需要独占访问。这样可以提高读操作的并发性能。

以上是常见的线程同步机制,具体使用哪种机制取决于具体的应用场景和需求。需要根据具体情况选择最适合的同步机制来保证线程安全和避免竞态条件。

在Java中,如何创建线程?谈谈继承Thread类与实现Runnable接口的区别。

在Java中,可以通过继承Thread类或实现Runnable接口来创建线程。它们的主要区别如下:

  1. 继承Thread类:继承Thread类是创建线程的一种方式。通过继承Thread类,子类可以直接重写Thread类中的run()方法来定义线程的执行逻辑。然后可以创建子类的实例,并调用start()方法来启动线程。示例代码如下:
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行逻辑}
}// 创建线程实例并启动线程
MyThread thread = new MyThread();
thread.start();
  1. 实现Runnable接口:实现Runnable接口也是创建线程的一种方式。通过实现Runnable接口,需要实现接口中的run()方法来定义线程的执行逻辑。然后可以创建Runnable接口的实例,并创建Thread对象,将Runnable实例作为参数传递给Thread的构造函数。最后调用Thread对象的start()方法来启动线程。示例代码如下:
public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行逻辑}
}// 创建Runnable实例和Thread实例,并启动线程
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

继承Thread类和实现Runnable接口的区别主要体现在继承和实现的关系上和代码结构上:

  • 继承Thread类是一种"is-a"关系,子类是Thread类的特殊化;实现Runnable接口是一种"has-a"关系,类包含了Runnable对象。在继承Thread类时,子类无法再继承其他类,而实现Runnable接口可以避免这个限制。

  • 继承Thread类需要重写run()方法,而实现Runnable接口需要实现run()方法。在继承Thread类时,线程的执行逻辑直接在子类中定义;而在实现Runnable接口时,线程的执行逻辑被封装在Runnable对象中,增加了代码的可复用性和灵活性。

通常来说,推荐使用实现Runnable接口的方式来创建线程,因为它可以避免单继承的限制,更灵活和可扩展。

Java中synchronized关键字的作用是什么?它有几种使用方式?

在Java中,synchronized关键字用于实现线程的同步,主要作用是保证多个线程对共享资源的安全访问。synchronized关键字有以下几种使用方式:

  1. 同步实例方法:在方法的声明中使用synchronized关键字,表示该方法是一个同步实例方法,同一时间只能被一个线程访问。示例代码如下:
public synchronized void synchronizedMethod() {// 方法逻辑
}
  1. 同步静态方法:在静态方法的声明中使用synchronized关键字,表示该静态方法是一个同步静态方法,同一时间只能被一个线程访问。示例代码如下:
public static synchronized void synchronizedStaticMethod() {// 方法逻辑
}
  1. 同步代码块:使用synchronized关键字对代码块进行同步,只有拥有所指定对象的锁的线程才能执行该代码块。示例代码如下:
public void synchronizedBlock() {synchronized (this) {// 代码块逻辑}
}
  1. 同步指定对象:可以使用synchronized关键字指定一个对象来进行同步,只有拥有该对象的锁的线程才能访问同步代码块。示例代码如下:
public void synchronizedObject() {Object lock = new Object();synchronized (lock) {// 代码块逻辑}
}

在使用synchronized关键字时,需要注意以下几点:

  • 每个对象都有一个内置锁(也称为监视器锁或管程),synchronized关键字就是基于该锁实现的。

  • 同步实例方法和同步静态方法的锁是不同的,同步实例方法的锁是该方法所属对象的实例,而同步静态方法的锁是该方法所属的类对象。

  • 同步代码块和同步指定对象都可以指定一个对象作为锁,但该对象必须是被所有需要同步的线程所共享的。

  • 使用synchronized关键字会带来一定的性能开销,因为需要获取和释放锁,所以应该避免过多的同步操作,合理使用锁来保证线程安全。

解释一下volatile关键字的作用及其与synchronized的区别。

volatile关键字是Java中用来修饰变量的关键字,它的主要作用是保证变量对所有线程的可见性和防止指令重排序。它有以下两个作用:

  1. 可见性:当一个变量被volatile修饰时,任何线程对该变量的修改都立即可见,即当一个线程修改了volatile变量的值,其他线程立即可见该变量的最新值。这是因为volatile修饰的变量会直接从主内存中读取和写入,而不会使用线程的本地缓存。

  2. 防止指令重排序:volatile关键字可以防止指令重排序,保证指令按照代码的顺序执行。这在多线程环境下特别重要,避免了因为指令重排序导致的线程安全问题。

与synchronized关键字相比,volatile关键字有以下几个区别:

  1. 锁的粒度:synchronized关键字是基于锁来实现同步,它可以修饰方法和代码块,可以控制对共享资源的访问。而volatile关键字是修饰变量的,它只能保证对变量的单个读写操作的原子性和可见性。因此,synchronized关键字可以对一段代码进行加锁,而volatile关键字只能修饰某个变量。

  2. 同步性:synchronized关键字是一种排他性的同步机制,同一时刻只能有一个线程获得锁并执行对共享资源的操作,其他线程需要等待。而volatile关键字是一种轻量级的同步机制,不会引起线程阻塞。

  3. 原子性:synchronized关键字可以保证对多个操作的原子性,即在同步代码块内对多个变量的操作都是原子的。而volatile关键字只能保证单个操作的原子性,对于复合操作需要通过其他手段来保证。

总结来说,synchronized关键字提供了更强的线程同步能力,可以保证更复杂的线程安全问题,而volatile关键字则提供了更轻量级的同步机制,主要用于保证变量的可见性和防止指令重排序。根据具体的业务需求,可以选择合适的关键字来实现线程同步。

什么是Java内存模型(JMM)?它如何影响多线程程序的执行?

Java内存模型(JMM)是Java虚拟机规范定义的一种抽象概念,它描述了Java程序中的线程如何与主内存和工作内存交互。JMM定义了一组规则和约束,用于保证多线程程序在各种平台上的可见性、原子性和有序性。

JMM影响多线程程序的执行主要体现在以下几个方面:

  1. 可见性:JMM保证了在一个线程修改共享变量后,其他线程能够立即看到这个修改。这是通过在线程之间对共享变量的读写操作进行同步来实现的,例如使用volatile关键字或synchronized关键字。

  2. 原子性:JMM保证了对于共享变量的一些特定操作是原子性的,即要么完全执行成功,要么不执行。这是通过使用synchronized关键字或Lock类实现的,对于多个操作需要作为一个原子操作的情况,可以使用这些同步机制来保证。

  3. 有序性:JMM保证了程序的执行顺序按照代码的顺序执行,对于不同线程中的操作,JMM可能对其进行指令重排序。但是,JMM也规定了对于特定的操作(如volatile变量的读写、synchronized关键字的加锁和解锁操作等),不能进行指令重排序,以保证有序性。

通过Java内存模型,开发者能够编写多线程程序时更加方便地进行并发编程。但是,由于JMM的规则比较复杂,开发者需要深入理解JMM的原理和规范,以避免并发编程中的各种问题,如线程安全问题、内存泄漏等。

谈谈wait(), notify(), 和notifyAll()方法的使用场景及注意事项。

wait(), notify() 和 notifyAll() 是在 Java 中用于线程间通信的方法。

  1. wait() 方法:调用 wait() 方法会使当前线程进入等待状态,并释放当前对象的锁。通常情况下,wait() 方法应该在 synchronized 块中被调用。它的使用场景包括:

    • 等待某个条件满足后再继续执行。
    • 实现线程间的协调与合作。
  2. notify() 方法:调用 notify() 方法会唤醒正在等待此对象锁的线程中的一个线程。被唤醒的线程仍然需要竞争锁。notify() 方法的使用场景包括:

    • 线程 B 在线程 A 执行某个操作后需要被唤醒。
    • 与 wait() 方法配合使用,实现线程间的协调与合作。
  3. notifyAll() 方法:与 notify() 方法类似,但是会唤醒所有正在等待此对象锁的线程。notifyAll() 方法的使用场景包括:

    • 通知所有等待某个条件的线程,以便它们重新评估条件是否满足。

注意事项:

  • 调用 wait(), notify() 和 notifyAll() 方法的线程必须先对当前对象获得锁。
  • wait(), notify() 和 notifyAll() 方法只能在同步代码块或同步方法中被调用。
  • 在调用 wait() 方法后,当前线程会释放锁,并且进入等待状态,直到被唤醒或被中断。
  • 在调用 notify() 或 notifyAll() 方法后,JVM 会从等待池中随机选择一个线程唤醒,但是具体唤醒哪个线程是不确定的。
  • 在使用 wait(), notify() 和 notifyAll() 方法时,应注意避免出现死锁、饥饿或竞争条件等问题。
  • 在多线程环境下,应该慎重使用 wait(), notify() 和 notifyAll() 方法,确保正确理解其作用,避免出现并发问题。

线程池的七个参数

  1. 核心线程数(corePoolSize):线程池中最小的线程数量,即使线程处于空闲状态也不会被销毁。

  2. 最大线程数(maximumPoolSize):线程池中允许存在的最大线程数量,当任务队列已满且线程数小于最大线程数时,会创建新的线程来执行任务。

  3. 线程空闲时间(keepAliveTime):当线程池中线程数量大于核心线程数且线程处于空闲状态时,空闲时间超过该参数设置的时间,线程会被销毁。

  4. 时间单位(unit):用于设置线程空闲时间的单位,如:秒、毫秒、分钟等。

  5. 任务队列(workQueue):用于存放待执行的任务的队列,当线程池中的线程数达到核心线程数后,多余的任务会被放入该队列中等待执行。

  6. 线程工厂(threadFactory):用于创建新的线程,默认使用默认的线程工厂。

  7. 拒绝策略(handler):当任务队列已满且线程池中的线程数已达到最大线程数时,新的任务将会被拒绝执行的策略。常见的拒绝策略有:抛出异常、丢弃任务、丢弃队列中等待最久的任务、调用线程执行任务等等。

线程安全的类和线程不安全的类分别都有哪些

线程安全的类是指在多线程环境下,可以保证多个线程同时访问该类的实例或静态方法时,不会出现不正确的结果。以下是一些常见的线程安全的类:

  1. ConcurrentHashMap:并发哈希表,支持高并发访问的线程安全的哈希表实现。
  2. CopyOnWriteArrayList:在写操作时进行复制,保证读写分离,适用于读多写少的场景。
  3. AtomicInteger:提供原子操作的整型变量,保证了对整型数据的原子性操作。
  4. AtomicLong:提供原子操作的长整型变量,保证了对长整型数据的原子性操作。
  5. ThreadLocal:为每个线程提供独立的变量副本,避免了多个线程之间的数据共享问题。

线程不安全的类是指在多线程环境下,多个线程同时访问该类的实例或静态方法时,可能导致不正确的结果,例如数据错乱、数据丢失等。以下是一些常见的线程不安全的类:

  1. ArrayList:在多个线程同时对列表进行修改时,可能导致数据不一致。
  2. HashMap:在多个线程同时对哈希表进行修改时,可能导致数据不一致或无法预测的结果。
  3. StringBuilder:在多线程环境下,由于非线程安全的操作,会导致字符串拼接错误。
  4. SimpleDateFormat:SimpleDateFormat 不是线程安全的,多个线程同时使用会导致日期格式错误。

使用线程安全的类可以避免在多线程环境下出现数据竞争和不一致的问题,而线程不安全的类则需要在多线程环境下使用额外的同步措施来保证线程安全。

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

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

相关文章

Verilog的逻辑系统及数据类型(二):参数和参数重载

目录 3.参数(parameters)3.1 参数重载(overriding)3.2 参数重载举例 微信公众号获取更多FPGA相关源码: 3.参数(parameters) 用参数声明一个可变常量,常用于定义延时及宽度变量。参数定义的语法:paramete…

安卓应用开发学习:获取经纬度及地理位置描述信息

前段时间,我在学习鸿蒙应用开发的过程中,在鸿蒙系统的手机上实现了获取经纬度及地理位置描述信息(鸿蒙应用开发学习:手机位置信息进阶,从经纬度数据获取地理位置描述信息)。反而学习时间更长的安卓应用开发…

adb remount fails - mount: ‘system‘ not in /proc/mounts 解决办法

mount -o rw,remount /挂载根 mount -o ro,remount /将状态重置为“ro” 以下是我个人的一些话 我热衷于在网络上分享我遇到的问题和解决方案。如果你有任何问题或需要帮助,欢迎留言交流,在共同学习的道路上一起进步。我很高兴结识那些在学习上积极进取…

常用框架-Spring Boot

常用框架-Spring Boot 1、Spring Boot是什么?2、为什么要使用Spring Boot?3、Spring Boot的核心注解是哪个?它主要由哪几个注解组成的?4、有哪些运行Spring Boot的方式?5、如何理解 Spring Boot 中的Starters?6、有哪些常见的Starters?7、如何在Spring Boot启动的时候运…

【WEB】关于react的WEB应用中使用React Developer Tools便捷快速查看元素数据

1、往扩展工具中添加React Developer Tools的扩展包 2、检查是否生效,如下图: 可以看到右上角多出来一个Components的tab选项,就是成功了

数据校验(JSR303、SpringBoot、自定义注解)

在一个项目中,不仅前端要对用户输入的数据进行校验,避免发送不必要的请求,而且后端也要对数据进行对应的校验,因为操作不都是通过页面过来的。 前端 不是很了解 正则表达式 配合各种组件使用 后端 这里以Java为例&#xff0…

winform 限制TextBox中只能输入正整数

txt_n是文本框的名字 private void txt_n_KeyPress(object sender, KeyPressEventArgs e){if (e.KeyChar ! \b)//这是允许输入退格键 {int len txt_n.Text.Length;if (len < 1 && e.KeyChar 0){e.Handled true ;}else if ((e.KeyChar < 0) || (e.KeyChar >…

WebStorm 环境配置带@符号的相对路径穿透

在使用WebStorm 环境开发web页面项目时有时想快速查看页面的引用代码&#xff0c;只能手工找到引入文件路径&#xff0c;这很不方便&#xff0c;只需通过配置webStorm单击打开。 1 使用符号相对路径&#xff0c;在默认情况下没有配置环境是无法打开&#xff0c;如下图&#xf…

AI全栈之coze的logo生成

前言 前几日体验了国产的AI-Agents产品coze 它是一种能够自主执行任务、与环境进行交互并根据所获取的信息做出决策和采取行动的软件程序 并且可以自己去创建属于自己的AIBot&#xff0c;还是很有意思的&#xff0c;大家可以去体验体验 在体验过程中&#xff0c;我发现在创…

适合爬虫开发用的性价比高的代理推荐

在爬虫开发过程中&#xff0c;使用代理可以有效地隐藏爬虫的真实来源&#xff0c;并绕过一些可能对爬虫设置的限制。然而&#xff0c;市面上的代理服务众多&#xff0c;选择一款性价比高且适合爬虫开发的代理服务显得尤为重要。以下是一些适合爬虫开发用的性价比比较高的代理推…

Linux操作系统进程同步的几种方式及基本原理

1&#xff0c;进程同步的几种方式 1.1信号量 用于进程间传递信号的一个整数值。在信号量上只有三种操作可以进行&#xff1a;初始化&#xff0c;P操作和V操作&#xff0c;这三种操作都是原子操作。 P操作(递减操作)可以用于阻塞一个进程&#xff0c;V操作(增加操作)可以用于…

【华为OD机试】递增字符串(C++/Java/Python)

题目 题目描述 [定义字符串]完全由 ‘A’ 和 ‘B’组成,当然也可以全是’A’或全是’B’。如果字符串从前往后都是以字典序排列的,那么我们称之为严格递增字符串。 给出一个字符串s,允许修改字符串中的任意字符,即可以将任何的’A’修改成’B’,也可以将任何的’B’修改成…

Go 实现继承的方式

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Java高级重点知识点-12-Collection、iterator迭代器、泛型

文章目录 Collection集合Iterator迭代器泛型&#xff08;难点&#xff09; Collection集合 集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 集合框架 单列集合java.util.Collection双列集合java.util.Map 集合类继承体系图&#xff1a; List集合的特点&am…

Interview preparation--Elasticsearch并发控制

Elasticsearch 并发控制 Elasticsearch是分布式的。创建&#xff0c;更新&#xff0c;删除文档时&#xff0c;必须将文档的新版本复制到集群中的其他节点。ES也是异步并行的&#xff0c;所有这些复制请求是并行发送的&#xff0c;并且可能不安顺序执行到每一个节点。ES需要一种…

新品发布 | TC1018Pro和TC1034Pro正式发布,功能升级,多设备时间同步

新品发布/New products release 同星智能最新推出TC1018Pro和TC1034Pro两款产品&#xff0c;新版本在保留原来基本功能的基础上做了升级&#xff0c;主要新增IO功能、错误帧ID检测、多设备间时间同步等功能。 接下来&#xff0c;让我们看看这两款产品带来了哪些具体功能升级&a…

玄奘取经线路矢量图分享

我们在《透过丝绸之路&#xff0c;看古人都走过哪些地方》一文中&#xff0c;为你分享过丝绸之路的矢量图数据。 现在&#xff0c;我们再为你分享一下玄奘取经线路的矢量图&#xff0c;你可以在文末查看这些数据的领取方式。 玄奘取经线路 《西游记》的故事相信大家都不陌生…

ABC234G Divide a Sequence 题解

题目来源 ABC234G 洛谷 Description 给定长度为 n n n 的序列 { a n } \{a_n\} {an​}。定义一种将 { a n } \{a_n\} {an​} 划分为若干段的方案的价值为每段的最大值减去最小值的差的乘积。求所有划分方案的价值的总和并对 998244353 998244353 998244353 取模。 1 ≤…

项目实训-vue(十一)

项目实训-vue&#xff08;十一&#xff09; 文章目录 项目实训-vue&#xff08;十一&#xff09;1.概述2.页顶导航栏3.导航信息4.总结 1.概述 本篇博客将记录我在图片上传页面中的工作。 2.页顶导航栏 <divstyle"display: flex;justify-content: space-between;alig…

如何挑选洗地机?盘点口碑最好的四大洗地机

在购买洗地机这种智能家电时&#xff0c;大家都应该格外谨慎。毕竟&#xff0c;洗地机价格不菲&#xff0c;精打细算&#xff0c;确保物尽其用才是最重要的。谁都不想花了高价买回来却让它闲置在墙角落灰尘。买之前我们还是需要对自己的需求做一个清晰的判断&#xff0c;实用性…