JUC并发编程(JUC核心类、TimeUnit类、原子操作类、CASAQS)附带相关面试题

目录

1.JUC并发编程的核心类

2.TimeUnit(时间单元)

3.原子操作类

4.CAS 、AQS机制


1.JUC并发编程的核心类

虽然java中的多线程有效的提升了程序的效率,但是也引发了一系列可能发生的问题,比如死锁,公平性、资源管理以及如何面对线程安全性带来的诸多危害。为此,java就提供了一个专门的并发编程包java.util.concurrent(简称JUC)。此包能够有效的减少了竞争条件和死锁问题。

以下介绍JUC包中核心的类

类名描述
ExecutorExecutor 是一个接口,定义了一种执行任务的方式,其目的是将任务的提交与任务的执行解耦。
ExecutorServiceExecutorServiceExecutor 的子接口,提供了更丰富的功能,例如线程池管理和任务提交等。
ScheduledExecutorServiceScheduledExecutorServiceExecutorService 的子接口,可以按照计划(时间或延迟)来执行任务。
CompletionServiceCompletionService 是一个用于异步执行任务并获取已完成任务结果的框架。
CallableCallable 是一个代表可以返回结果或抛出异常的任务的接口。它类似于 Runnable 接口,但具有返回值。
FutureFuture 是一个可用于获取异步计算结果的接口。
ReentrantLockReentrantLock 是一个可重入锁,它提供了更灵活的同步控制和更高级别的功能。
BlockingQueueBlockingQueue 是一个支持阻塞操作的队列,提供了线程安全的生产者-消费者模式的实现。
CountDownLatchCountDownLatch 是一个同步辅助类,允许一个或多个线程等待其他线程完成操作后再继续执行。
CyclicBarrierCyclicBarrier 是一个同步辅助类,使得一组线程能够互相等待,直到所有线程都达到某个公共屏障点。


2.TimeUnit(时间单元)

这个类能够非常好的让我们实现各种时间之间的转换。TimeUnit类的是枚举类,里面有DAYS(天),HOURS(小时),MINUTES(分钟),SECONDS(秒),MILLISECONDS(毫秒),NANNOSECONDS(纳秒)

TimeUnit类中常用的方法:

方法签名描述
public long convert(long sourceDuration, long srcDuration)该方法用于将给定的时间源持续时间转换为目标持续时间。
public void sleep(long timeout) throws InterruptedException该方法使当前线程进入休眠状态,暂停执行一段指定的时间(以毫秒为单位)。如果在休眠期间中断了线程,则会抛出 InterruptedException 异常。

具体应用案例:

1.时间转换与输出一个月后的日期

package Example2101;import java.util.Date;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {
//        五个小时时间long hours = 5;
//        通过SECONDS类将5个小时转为秒long seconds = TimeUnit.SECONDS.convert(hours,TimeUnit.HOURS);System.out.println(seconds);//        获取当前时间long now = System.currentTimeMillis();long furture = now + TimeUnit.MILLISECONDS.convert(30,TimeUnit.DAYS);System.out.println("Now Time is"+new Date(now));Date futureDay = new Date(furture);System.out.println("after mounth time is"+futureDay);}
}

 

案例2:定义一个闹钟,这个闹钟在5天后会自动发送消息

这种闹钟形式可以通过线程的睡眠机制进行完成,但是一般情况下如果使用线程的睡眠Thread.sleep()里面放的是毫秒,如果要睡眠五天,那么需要设置的数值会非常非常大的,所以可以使用TimeUnit类的睡眠方法实现自定义睡眠。

package Example2102;import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {new Thread(()->{try {
//                通过TimeUnit下的Days类的sleep函数定义五天时间TimeUnit.DAYS.sleep(5);System.out.println("闹钟响了!!!!!!");}catch (InterruptedException e){e.printStackTrace();}},"闹钟").start();}
}

3.原子操作类

问题引出:一般情况下如果多线程进行竞争一个变量时候会引发数据错乱的问题。比如多线程下售票员售票案例,由于多个线程竞争,一张票可能已经被卖出去了,但是其他的售票员并不知道,继续售卖同一张票。在之前的时候我们通过了Sychronized()同步位解决了这个问题。但是用这个方法也有不小的弊端,那就是程序效率会大大下降。为此JUC提供了一个新的方式解决这个问题,那就是原子操作类。

首先理解原子性,原子是不可分割的最小物体,在编程中是指一种操作要么做了,要么不做。不可以中断的一种操作。原子操作类具有更高效率,更安全,更简单用法

原子操作类分为很多类,大致分为4类:

基本类型:AtomicInteger 、AtomicLong、AtomicBoolean

数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference;

对象属性修改类型:

AtomicIntegerFieldUpdater;AtomicLongFiledUpdater;AtomicReferenceFieldUpdater;

 1.基本类型的原子操作类

基本类型:AtomicInteger 、AtomicLong、AtomicBoolean

基本类型之间的操作是差不多的,这里用AtomicLong举例

AtomicLong的常用方法

方法描述
AtomicLong(long initValue)创建一个新的AtomicLong实例,并设置初始值为initValue。
get()获取当前存储在AtomicLong中的值。
set(long newValue)将AtomicLong的值设置为newValue。
getAndIncrement()先获取当前存储在AtomicLong中的值,然后将AtomicLong的值增加1。返回先前的值。
setAndIncrement()将AtomicLong的值增加1。返回增加前的值。
decrementAndGet()将AtomicLong的值减少1,并返回减少后的值。

使用类方法的关键就在于熟悉add(增加) decrement(自减)increment(自增) set(设置值) get(获取类内部的数据) 方法就是这几个操作之间的组合

案例代码:多个售票员售卖100张票

package Example2103;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class javaDemo {public static void main(String[] args) {
//        创建原子操作类AtomicInteger ticket = new AtomicInteger(6);AtomicInteger flag = new AtomicInteger(1);//标志还有票//        创建三个线程进行售票for (int i =0;i<3;i++){new Thread(()->{while (ticket.get()>0){System.out.println("售票员"+Thread.currentThread().getName()+"售卖第"+ticket.decrementAndGet()+"张票");try {
//                        设置两秒睡眠TimeUnit.SECONDS.sleep(2);}catch (Exception e){e.printStackTrace();}
//                    如果没有票了就将标志位的值设置为0,表示没有票了if (ticket.get() == 0){flag.set(0);System.out.println("卖完了");}}}).start();}}
}

 

 可以看到即使没有使用同步机制也实现了同步的效果。

2.数组原子操作类

数组原子操作类有:AtomicArrayInteger AtomicLongArray AtomicReferenceArray(对象数组)

由于三者这件的使用区别不大,所以这里展示AtomicReferenceArray

AtomicReferenceArray常用方法:

方法描述
AtomicReferenceArray(int length)构造一个指定长度的AtomicReferenceArray对象。
AtomicReferenceArray(E[] array)使用给定数组初始化AtomicReferenceArray对象。
int length()返回AtomicReferenceArray的长度(即元素个数)。
boolean compareAndSet(int index, E expect, E update)将指定索引位置的元素与期望值进行比较,如果相等,则将其更新为新的值。该操作是原子性的,返回是否更新成功。
E get(int index)获取指定索引位置的元素的值。
void set(int index, E newValue)设置指定索引位置的元素的值为newValue。
E getAndSet(int index, E newValue)获取指定索引位置的元素的当前值,并将其设置为newValue。

 案例代码:

package Example2104;import java.util.concurrent.atomic.AtomicReferenceArray;public class javaDemo {public static void main(String[] args) {String data[] = new String[]{"王二狗","180","130"};
//        初始化AtomicReferenceArray<String> array = new AtomicReferenceArray<String>(data);
//        对象数组的操作System.out.println("身高是:"+array.get(1));array.set(2,"150");System.out.println(array.get(0)+"在拼命锻炼后体重变成:"+array.get(2));
//        筛选如果名字是王二狗的自动改名王二array.compareAndSet(0,"王二狗","王二");System.out.println("改名后名字叫"+array.get(0));}
}

 3.引用原子操作类

引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference;

其中AtomicReference是可以直接引用数据类型的原子性操作

下面是AtomicReference的常用方法:

方法描述
AtomicReference()无参构造方法,创建一个初始值为null的AtomicReference对象。
V get()获取当前AtomicReference对象持有的值。
void set(V newValue)设置AtomicReference对象的值为newValue。
boolean compareAndSet(V expect, V update)将AtomicReference对象的值与期望值expect进行比较(==比较),如果相等,则将其更新为新值update。该操作是原子性的,返回是否更新成功。
V getAndSet(V newValue)先获取当前AtomicReference对象的值,然后将其设置为newValue,并返回原来的值。

案例代码:使用AtomicReference进行引用操作

package Example2106;import java.util.concurrent.atomic.AtomicReference;
// 创建普通人类
class Person{private int age;private String name;private int id;Person(int age,String name,int id){this.age = age;this.name = name;this.id = id;}
}public class javaDemo {public static void main(String[] args) {Person person1 = new Person(18,"张三",001);Person person2 = new Person(20,"王思",1002);
//        传入person1对象AtomicReference<Person> person = new AtomicReference<Person>(person1);
//        输出对象地址System.out.println(person.get());
//        更改引用对象person.set(person2);System.out.println(person.get());}
}

AtomicStampedReference 基于版本号的数据引用。其中版本号是自己定义的int数据类型

下面是AtomicStampedReference的常用方法:

方法描述
AtomicStampedReference(V initRef, int initStamp)构造一个AtomicStampedReference对象,初始引用值为initRef,初始标记值(戳)为initStamp。
V getReference()获取当前AtomicStampedReference对象持有的引用值。
void set(V newRef, int newStamp)设置AtomicStampedReference对象的引用值为newRef,标记值(戳)为newStamp。
boolean compareAndSet(V expectRef, V newRef, int expectStamp, int newStamp)将AtomicStampedReference对象的引用值与期望值expectRef、标记值(戳)与期望值expectStamp进行比较,如果相等,则将其更新为新值newRef和newStamp。该操作是原子性的,返回是否更新成功。
int attemptStamp(V expectedReference, int newStamp)如果当前引用值等于expectedReference,则尝试将标记值(戳)更新为newStamp。如果更新成功,返回新的标记值(戳),否则返回当前标记值。
int getStamp()获取当前AtomicStampedReference对象持有的标记值(戳)。

案例代码:

package Example2107;import java.util.concurrent.atomic.AtomicStampedReference;class Draw{private String content = "";private String autor = "";private String title ="";Draw(String content,String autor,String title){this.content =content;this.autor = autor;this.title = title;}public void setContent(String content) {this.content = content;}public String getContent() {return content;}
}
public class javaDemo {public static void main(String[] args) {Draw  draw1= new Draw("","alphaMilk","JUC并发编程原子操作类");
//        初始化内容,版本号为1AtomicStampedReference<Draw> atomicDraw = new AtomicStampedReference<Draw>(draw1,1);System.out.println(atomicDraw.getReference());
//        更新内容,版本号更改draw1.setContent("Hello,word");atomicDraw.set(draw1,2);
//        获取当前版本System.out.println(atomicDraw.getStamp());}
}

AtomicMarkableReference与AtomicStampedReference的区别在于,一个是设置boolean类型的初始化标记,一个多设置的是int类型版本号

下面是AtomicMarkableReference的常用方法:

方法描述
AtomicMarkableReference(V initRef, boolean initMark)构造一个AtomicMarkableReference对象,初始引用值为initRef,初始标记值为initMark。
V getReference()获取当前AtomicMarkableReference对象持有的引用值。
boolean isMarked()判断当前AtomicMarkableReference对象是否被标记。
boolean compareAndSet(V expectRef, V newRef, boolean expectMark, boolean newMark)将AtomicMarkableReference对象的引用值与期望值expectRef、标记值与期望值expectMark进行比较,如果相等,则将其更新为新值newRef和newMark。该操作是原子性的,返回是否更新成功。
void set(V newRef, boolean newMark)设置AtomicMarkableReference对象的引用值为newRef,标记值为newMark。
boolean attemptMark(V expectedReference, boolean newMark)如果当前引用值等于expectedReference,则尝试将标记值更新为newMark。如果更新成功,返回true,否则返回false。

案例代码:

一个班统计同学是否交了班费

package Example2108;import java.util.concurrent.atomic.AtomicMarkableReference;class  Student{private String name;private int id;Student(String name,int id){this.name = name;this.id = id;}
}
public class javaDemo {public static void main(String[] args) {Student stu1 = new Student("王一",001);Student stu2 = new Student("张二蛋",002);
//        王一交过班费AtomicMarkableReference<Student> atoStu = new AtomicMarkableReference<Student>(stu1,true);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println("该同学交过班费");}else System.out.println("该同学尚未交过班费");
//        张二蛋没有交班费atoStu.set(stu2,false);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println("该同学交过班费");}else System.out.println("该同学尚未交过班费");}
}

4.对象属性修改原子类

 AtomicIntegerFieldUpdater;AtomicLongFiledUpdater;AtomicReferenceFieldUpdater;

这三个类的实现原理基本差不多,所以将用AtomicIntegerFieldUpdater举例:

以下是AtomicIntegerFieldUpdater类的常用方法:

int addAndGet(T obj, int data)将指定对象obj的字段值与data相加,并返回相加后的结果。
boolean compareAndSet(T obj, int expect, int update)将指定对象obj的字段值与期望值expect进行比较,如果相等,则将其更新为新值update。返回是否更新成功。
int get(T obj)获取指定对象obj的字段值。
int getAndSet(T obj, int newValue)获取指定对象obj的字段值,并将其设置为新值newValue。
int decrementAndGet(T obj)将指定对象obj的字段值减1,并返回减1后的结果。
int incrementAndGet(T obj)将指定对象obj的字段值加1,并返回加1后的结果。

 案例代码:

package Example2109;import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;class Book{volatile long id;volatile String name;Book(long id, String name){this.id = id;this.name = name;}}public class javaDemo {public static void main(String[] args) {Book book1 = new Book(114514,"Java从入门到入土");AtomicReferenceFieldUpdater<Book,String> bookmanger = AtomicReferenceFieldUpdater.newUpdater(Book.class,String.class,"name");System.out.println("更新前书本名称为:"+bookmanger.get(book1));bookmanger.set(book1,"Java从入门到项目实战");System.out.println("更新后书本名称为:"+bookmanger.get(book1));}
}

 


4.CAS 、AQS机制

CAS是一条CPU并发原语。它的功能是判断某个内存某个位置的值是否相等,如果是则改为新的值,这个操作过程属于原子性操作。

CAS是乐观锁,是一种冲突重试机制,在并发竞争不是很剧烈的情况下,其操作性能会好于悲观锁机制(Synchronization同步处理)

*面试题为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性?

  1. Synchronized的并发策略是悲观的,不管是否产生竞争,任何数据的操作都必须加锁。
  2. 乐观锁的核心是CAS,CAS包括内存值、预期值、新值,只有当内存值等于预期值时,才会将内存值修改为新值。

*面试题:乐观锁一定就是好的吗?

  1. 乐观锁认为对一个对象的操作不会引发冲突,所以每次操作都不进行加锁,只是在最后提交更改时验证是否发生冲突,如果冲突则再试一遍,直至成功为止,这个尝试的过程称为自旋。
  2. 乐观锁没有加锁,但乐观锁引入了ABA问题,此时一般采用版本号进行控制;
  3. 也可能产生自旋次数过多问题,此时并不能提高效率,反而不如直接加锁的效率高;
  4. 只能保证一个对象的原子性,可以封装成对象,再进行CAS操作;

*面试题:volatile 关键字的作用

对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger。

volatile 常用于多线程环境下的单次操作(单次读或者单次写)。
 

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

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

相关文章

Redis—持久化

这里写目录标题 AOF三种写回策略写回策略的优缺点AOF 重写机制AOF后台重写AOF优缺点使用命令 RDBRDB 持久化的工作原理执行快照时&#xff0c;数据能被修改吗RDB 持久化的优点RDB 持久化的缺点 混合持久化大key对持久化的影响 AOF 保存写操作命令到日志的持久化方式&#xff0…

MyBatis核心 - SqlSession如何通过Mapper接口生成Mapper对象

书接上文 MyBatis – 执行流程 我们通过SqlSession获取到了UserMapper对象&#xff0c;代码如下&#xff1a; // 获取SqlSession对象 SqlSession sqlSession sqlSessionFactory.openSession();// 执行查询操作 try {// 获取映射器接口UserMapper userMapper sqlSession.get…

什么CRM客户管理系统好用?公司规模不大,有推荐吗?

CRM客户管理系统是什么&#xff1f; 一句话来概括&#xff1a;CRM是客户关系管理的缩写&#xff0c;指企业通过建立客户档案、跟进客户需求、提供优质服务来维系客户关系的一种管理模式。通常我们认知中的CRM管理系统软件&#xff0c;往往作用于企业的三个流程&#xff1a; 1…

机器学习笔记之优化算法(十)梯度下降法铺垫:总体介绍

机器学习笔记之优化算法——梯度下降法铺垫&#xff1a;总体介绍 引言回顾&#xff1a;线搜索方法线搜索方法的方向 P k \mathcal P_k Pk​线搜索方法的步长 α k \alpha_k αk​ 梯度下降方法整体介绍 引言 从本节开始&#xff0c;将介绍梯度下降法 ( Gradient Descent,GD ) …

学习总结(TAT)

好久都没交总结了&#xff0c;今天把之前的思路和错误整理了一下&#xff1a; 在服务器和客户端两侧&#xff0c;不可以同时先初始化获取输入流&#xff0c;否则会造成堵塞&#xff0c;同时为这位作者大大打call&#xff1a; (3条消息) 关于Java Socket和创建输入输出流的几点…

实战项目ssm权限系统 3-自定义注解+AOP拦截器记录登录日志

一 登录日志以及操作操作日志的操作 1.1 登录日志配置收集 1.1.1 编写log入库的service层接口 1.接口&#xff1a;在spring-security模块中 2.实现类&#xff1a;在service-system模块中 3.dao层&#xff1a;在service-system模块中 1.1.2 过滤器添加log记录 在过滤器中&…

01_什么是ansible、基本架构、ansible工作机制、Ansible安装、配置主机清单、设置SSH无密码登录等

1.什么是ansible 1.1.基本介绍 1.2.基本架构 1.3.基本特征 1.4.优点 1.5.ansible工作机制 2.Ansible安装 2.1.机器准备 2.2.安装ansible 2.2.1.安装epel源 2.2.2.安装ansible 2.2.3.查看ansible版本 2.2.4.树状结构展示文件夹 2.2.4.1.其中ansible.cfg的内容如下 2.2.4.2.host的…

ts中interface自定义结构约束和对类的约束

一、interface自定义结构约束对后端接口返回数据 // interface自定义结构 一般用于较复杂的结构数据类型限制 如后端返回的接口数据// 首字母大写;用分割号隔开 interface Iobj{a:number;b:string } let obj:Iobj {a:1,b:2 }// 复杂类型 模拟后端返回的接口数据 interface Il…

管理类联考——逻辑——论证逻辑——汇总篇——真题和典例——削弱

削弱 199-2014-10-41——割裂关系 卫计委的报告表明&#xff0c;这些年来医疗保健费的确是增加了。可见&#xff0c;我们每个人享受到的医疗条件大大改善了。 以下哪项对上述结论提出最严重的质疑? A.医疗保健费的绝大部分用在了对高危病人的高技术强化护理上。 B.在不增加费…

Spring事务管理

目录 1.什么是事务 事务的四大特性&#xff08;ACID&#xff09; 2.Spring中的事务 2.1PlatformTransactionManager 2.2TransactionDefinition 2.3TransactionStatus 3.编程式事务 4.声明式事务&#xff08;Transactional&#xff09; 5. 事务属性 5.1 隔离性(isolat…

Linux-GPIO 配置pull up、pull down、no pull

author daisy.skye的博客_CSDN博客-Qt,嵌入式,Linux领域博主 https://blog.csdn.net/qq_40715266?typeblog 系列基于RK3568的Linux驱动开发——GPIO知识点&#xff08;一&#xff09;_daisy.skye的博客-CSDN博客基于RK3568的Linux驱动开发—— GPIO知识点&#xff08;二&#…

【VisualGLM】大模型之 VisualGLM 部署

目录 1. VisualGLM 效果展示 2. VisualGLM 介绍 3. VisualGLM 部署 1. VisualGLM 效果展示 VisualGLM 问答 原始图片 2. VisualGLM 介绍 VisualGLM 主要做的是通过图像生成文字&#xff0c;而 Stable Diffusion 是通过文字生成图像。 一种方法是将图像当作一种特殊的语言进…

C语言刷题------(2)

C语言刷题——————&#xff08;2&#xff09; 刷题网站&#xff1a;题库 - 蓝桥云课 (lanqiao.cn) First Question&#xff1a;时间显示 题目描述 小蓝要和朋友合作开发一个时间显示的网站。 在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表…

Nginx与docker配置安装

目录&#xff1a; Nginx的安装配置&#xff1a; 1、安装依赖包&#xff1a; 2、下载Nginx安装包&#xff1a; 3、解压Nginx压缩包&#xff1a; 4、配置Nginx编译环境&#xff1a; 5、编译并安装Nginx&#xff1a; 6、安装完Nginx后&#xff0c;可以切换到Nginx的安装目录…

python版《羊了个羊》游戏开发第一天

Python小型项目实战教学课《羊了个羊》 一、项目开发大纲&#xff08;初级&#xff09; 版本1.0&#xff1a;基本开发 课次 内容 技术 第一天 基本游戏地图数据 面向过程 第二天 鼠标点击和移动 面向对象 第三天 消除 设计模式&#xff1a;单例模式 第四天 完整…

clion使用qDebug()控制台无输出的可能解决方法

给项目添加一个环境变量 QT_ASSUME_STDERR_HAS_CONSOLE1参考网址&#xff1a;https://youtrack.jetbrains.com/issue/CPP-24369/Auto-enable-qDebug-console.log-output-to-the-debug-console-for-Qt-projects-on-Windows

Flutter:文件读取—— video_player、chewie、image_picker、file_picker

前言 简单学习一下几个比较好用的文件读取库 video_player 简介 用于视频播放 官方文档 https://pub-web.flutter-io.cn/packages/video_player 安装 flutter pub add video_player加载网络视频 class _MyHomePageState extends State<MyHomePage> {// 控制器late…

Android 开发者选项日志存储路径

android开发者选项中存在两个item是关于系统日志的。 1.日志记录器缓冲区大小 2.在设备上永久存储日志记录器数据 一个是用来设置缓冲区大小&#xff0c;一个是用来日志存储开关及过滤。 通过分析 system/core/logcat/logcatd.rc mkdir /data/misc/logd 0770 logd log 日志的…

ArcGIS Pro基础:【划分】工具实现等比例、等面积、等宽度划分图形操作

本次介绍【划分】工具的使用&#xff0c;如下所示&#xff0c;为该工具所处位置。使用该工具可以实现对某个图斑的等比例面积划分、相等面积划分和相等宽度划分。 【等比例面积】&#xff1a;其操作如下所示&#xff0c;其中&#xff1a; 1表示先选中待处理的图斑&#xff0c;2…

华为智选首款纯电轿跑“LUXEED”能大卖吗?

监制 | 何玺 排版 | 叶媛 华为智选纯电轿跑来袭&#xff01; 8月7日&#xff0c;华为常务董事余承东在社交媒体上发文&#xff0c;宣布华为智选即将推出首款“突破想象”的纯电轿跑车。 01 华为智选首款纯电轿跑来袭 余承东的发文引起了极大关注&#xff0c;在各大媒体的报…