• 正文
  • 相关推荐
申请入驻 产业图谱

面经 | 入职腾讯一年,终于调薪了!

08/09 08:25
1582
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是小林。

腾讯年中调薪结果出来了,看到不少人都在聊这个,今天咱就说说这次调薪的情况。

根据网上不少人爆料,这次腾讯调薪大概分了几个档,具体是这样:

    第一档:绩效双 5 星,调薪 5 - 9k 左右,有期权第二档:绩效单 5 星或核心业务 3 星,调薪 2 - 4k 左右,可能有期权第三档:常年 3 星,少量调薪,无期权其他:子公司表现优异,调薪 1k 左右,无期权

我还问去年毕业去腾讯工作一年的训练营同学,他跟反馈他组里情况,如果绩效相同的情况,感觉毕业生base低的涨的多,base高的涨的少,所以低base加入腾讯的同学,也是有机会base慢慢提升起来的,甚至有机会超过当初sp、ssp加入的校招同学。

往年腾讯职级晋升时,涨薪情况有点不一样。9 升 10 的话,多数人涨 5000-8000,最少 4000,最多快 1w,还大概率能拿到 15 万以上的股票。8 升 9,多数涨 3000-4000,最少 2000,最多 6000 多。5 升 6,多数涨 1000-2000,最少 500。

以前职级晋升基本对应涨薪,现在不同了,受部门、项目、绩效多重影响,比如核心业务(如 AI、游戏)员工,绩效稍好,调薪更优;在重点项目表现突出,涨薪幅度大 。然后部门拖后腿、个人绩效差,可能调薪无望。

腾讯这些年在 AI 等领域投入不少,所以第一档才会看重这方面的成绩,就是想鼓励大家往这些方向努力。这次调薪,有人开心有人觉得一般。拿到高调薪和期权的,肯定挺高兴;调得少的,可能得想想以后的发展了。

接下来咱们来看看腾讯天美的校招面经,也就是王者荣耀所在的bg,这是一面的面经,问的比较基础,技术问题也没有很多,难度中规中矩。

腾讯天美一面凉经

ArrayList 用过吗?是线程安全的吗?

用过,ArrayList基于动态数组实现,它允许快速的随机访问,即通过索引访问元素的时间复杂度为 O (1)。在添加和删除元素时,如果操作位置不是列表末尾,可能需要移动大量元素,性能相对较低。适用于需要频繁随机访问元素,而对插入和删除操作性能要求不高的场景,如数据的查询和展示等。

ArrayList 不是线程安全的,在高并发添加数据下,ArrayList会暴露三个问题;

    部分值为null(我们并没有add null进去)索引越界异常size与我们add的数量不符

为了知道这三种情况是怎么发生的,ArrayList,add 增加元素的代码如下:

publicbooleanadd(E e){
? ? ? ? ensureCapacityInternal(size +?1); ?// Increments modCount!!
? ? ? ? elementData[size++] = e;
? ? ? ??returntrue;
? ? }

ensureCapacityInternal()这个方法的详细代码我们可以暂时不看,它的作用就是判断如果将当前的新元素加到列表后面,列表的elementData数组的大小是否满足,如果size + 1的这个需求长度大于了elementData这个数组的长度,那么就要对这个数组进行扩容。

大体可以分为三步:

    判断数组需不需要扩容,如果需要的话,调用grow方法进行扩容;将数组的size位置设置值(因为数组的下标是从0开始的);将当前集合的大小加1

下面我们来分析三种情况都是如何产生的:

    部分值为null:当线程1走到了扩容那里发现当前size是9,而数组容量是10,所以不用扩容,这时候cpu让出执行权,线程2也进来了,发现size是9,而数组容量是10,所以不用扩容,这时候线程1继续执行,将数组下标索引为9的位置set值了,还没有来得及执行size++,这时候线程2也来执行了,又把数组下标索引为9的位置set了一遍,这时候两个先后进行size++,导致下标索引10的地方就为null了。索引越界异常:线程1走到扩容那里发现当前size是9,数组容量是10不用扩容,cpu让出执行权,线程2也发现不用扩容,这时候数组的容量就是10,而线程1 set完之后size++,这时候线程2再进来size就是10,数组的大小只有10,而你要设置下标索引为10的就会越界(数组的下标索引从0开始);size与我们add的数量不符:这个基本上每次都会发生,这个理解起来也很简单,因为size++本身就不是原子操作,可以分为三步:获取size的值,将size的值加1,将新的size值覆盖掉原来的,线程1和线程2拿到一样的size值加完了同时覆盖,就会导致一次没有加上,所以肯定不会与我们add的数量保持一致的;

怎么保证 ArrayList 的线程安全?

要保证?ArrayList 的线程安全,可以采用以下几种方式:

通过Collections 工具类的synchronizedList() 方法可以将ArrayList 包装为线程安全的列表。该方法会返回一个同步(线程安全)的列表,所有的增删改查操作都会通过同步机制(synchronized)保证线程安全。

import?java.util.ArrayList;
import?java.util.Collections;
import?java.util.List;

publicclassSynchronizedListExample{
? ??publicstaticvoidmain(String[] args){
? ? ? ??// 创建线程安全的列表
? ? ? ? List<String> safeList = Collections.synchronizedList(new?ArrayList<>());
? ? ? ??
? ? ? ??// 多线程操作该列表
? ? ? ??new?Thread(() -> {
? ? ? ? ? ??for?(int?i =?0; i <?1000; i++) {
? ? ? ? ? ? ? ? safeList.add("Thread1-"?+ i);
? ? ? ? ? ? }
? ? ? ? }).start();
? ? ? ??
? ? ? ??new?Thread(() -> {
? ? ? ? ? ??for?(int?i =?0; i <?1000; i++) {
? ? ? ? ? ? ? ? safeList.add("Thread2-"?+ i);
? ? ? ? ? ? }
? ? ? ? }).start();
? ? }
}

CopyOnWriteArrayList

?是 Java 并发包(java.util.concurrent)提供的线程安全列表,其核心原理是:写入时复制。示例代码:

import?java.util.List;
import?java.util.concurrent.CopyOnWriteArrayList;

publicclassCopyOnWriteExample{
? ??publicstaticvoidmain(String[] args){
? ? ? ? List<String> safeList =?new?CopyOnWriteArrayList<>();
? ? ? ??
? ? ? ??// 多线程操作
? ? ? ??new?Thread(() -> {
? ? ? ? ? ??for?(int?i =?0; i <?1000; i++) {
? ? ? ? ? ? ? ? safeList.add("Thread1-"?+ i);
? ? ? ? ? ? }
? ? ? ? }).start();
? ? ? ??
? ? ? ??new?Thread(() -> {
? ? ? ? ? ??for?(int?i =?0; i <?1000; i++) {
? ? ? ? ? ? ? ? safeList.add("Thread2-"?+ i);
? ? ? ? ? ? }
? ? ? ? }).start();
? ? }
}
        • 通过手动添加同步锁(如synchronized?关键字或ReentrantLock),强制多线程串行访问ArrayList,从而保证线程安全。代码如下:
import?java.util.ArrayList;
import?java.util.List;

publicclassManualSyncExample{
? ??private?List<String> list =?new?ArrayList<>();
? ??
? ??// 所有操作都添加同步锁
? ??publicsynchronizedvoidadd(String item){
? ? ? ? list.add(item);
? ? }
? ??
? ??publicsynchronized?String?get(int?index){
? ? ? ??return?list.get(index);
? ? }
? ??
? ??publicsynchronizedintsize(){
? ? ? ??return?list.size();
? ? }
}

死锁问题怎么解决?

死锁只有同时满足以下四个条件才会发生:

互斥条件:互斥条件是指多个线程不能同时使用同一个资源。持有并等待条件:持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1。不可剥夺条件:不可剥夺条件是指,当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。环路等待条件:环路等待条件指的是,在死锁发生的时候,两个线程获取资源的顺序构成了环形链

避免死锁问题就只需要破环其中一个条件就可以,最常见的并且可行的就是使用资源有序分配法,来破环环路等待条件

那什么是资源有序分配法呢?线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

进程和线程的区别?

本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小

稳定性方面:进程中某个线程如果崩溃了,可能会导致整个进程都崩溃。而进程中的子进程崩溃,并不会影响其他进程。

内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源

包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线

线程共享的有什么?

同一进程中的所有线程共享进程的核心资源,主要包括:

代码段:进程的可执行代码,所有线程共享同一份程序指令。

数据段:全局变量、静态变量等,线程对这些变量的修改会被其他线程看到。

堆内存动态分配的内存(如 Java 中的new、C 中的malloc),属于进程共享区域。

文件描述符:打开的文件、网络连接等 IO 资源,线程可共享读写。

信号处理方式:进程注册的信号处理函数对所有线程生效。

线程也有少量私有资源,不与其他线程共享:

程序计数器(PC):记录当前线程执行的指令位置。

栈内存(Stack):存储局部变量、函数调用栈等,线程私有以避免冲突。

寄存器:CPU 中的寄存器状态,线程切换时需保存和恢复。

线程 ID:标识不同线程的唯一 ID。

jvm 怎么进行垃圾回收的?

垃圾回收的核心目标是识别并回收不再被使用的对象所占用的内存,避免内存泄漏和溢出。主要回收堆内存中不再被引用的对象(方法区的元数据也可能被回收,但频率较低)。

JVM 通过两种方式判断对象是否可回收:

引用计数法:给对象添加引用计数器,被引用时 + 1,引用失效时 - 1,计数器为 0 则标记为可回收。 缺点:无法解决循环引用问题(如 A 引用 B,B 引用 A,但两者都不再被其他对象引用)。

可达性分析(主流):以 "GC Roots" 为起点,遍历对象引用链,不可达的对象被标记为可回收。 GC Roots 包括:虚拟机栈中引用的对象、方法区中类静态属性 / 常量引用的对象、本地方法栈中 JNI 引用的对象等。

JVM 垃圾回收的核心逻辑是:通过可达性分析标记存活对象 → 根据分代策略(新生代 / 老年代)采用不同回收算法(复制 / 标记 - 清除 / 整理) → 释放无用对象内存。

MySQL 锁的分类?

在 MySQL 里,根据加锁的范围,可以分为全局锁、表级锁和行锁三类。

锁类型 加锁范围 加锁语句 具体说明
全局锁 整个数据库 flush tables with read lock 执行该语句后数据库处于只读状态,其他线程的增删改或表结构修改操作都会阻塞
表级锁 lock tables 对表加表锁,会限制别的线程的读写,也会限制本线程接下来的读写操作
表级锁(元数据锁) 自动加锁 对表进行 CRUD 操作时加 MDL 读锁;对表做结构变更操作时加 MDL 写锁
表级锁(意向锁) 执行插入、更新、删除操作时自动加锁 执行插入、更新、删除操作时,先对表加上「意向独占锁」,然后对该记录加独占锁
行级锁(记录锁) 表中的一条记录 InnoDB 引擎自动加锁 有 S 锁(共享锁)和 X 锁(排他锁)之分,满足读写互斥,写写互斥
行级锁(间隙锁) 表中的记录间隙 InnoDB 引擎自动加锁,只存在于可重复读隔离级别 用于解决可重复读隔离级别下幻读的现象
行级锁(Next-Key Lock) 表中的一个范围及记录本身 InnoDB 引擎自动加锁 是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身

全局锁:通过flush tables with read lock 语句会将整个数据库就处于只读状态了,这时其他线程执行以下操作,增删改或者表结构修改都会阻塞。全局锁主要应用于做

全库逻辑备份,这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

表级锁:MySQL 里面表级别的锁有这几种:表锁:通过lock tables 语句可以对表加表锁,表锁除了会限制别的线程的读写外,也会限制本线程接下来的读写操作。元数据锁:当我们对数据库表进行操作时,会自动给这个表加上 MDL,对一张表进行 CRUD 操作时,加的是MDL 读锁;对一张表做结构变更操作的时候,加的是MDL 写锁;MDL 是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。意向锁:当执行插入、更新、删除操作,需要先对表加上「意向独占锁」,然后对该记录加独占锁。

意向锁的目的是为了快速判断表里是否有记录被加锁

行级锁:InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的,满足读写互斥,写写互斥间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

手撕:反转链表迭代和递归实现

下面我将为你提供反转链表的两种实现方式:迭代法和递归法。

首先我们需要定义链表节点的结构,然后分别实现两种反转算法。

publicclassReverseLinkedList{
? ??// 定义链表节点
? ??staticclassListNode{
? ? ? ??int?val;
? ? ? ? ListNode next;
? ? ? ??
? ? ? ? ListNode(int?val) {
? ? ? ? ? ??this.val = val;
? ? ? ? ? ??this.next =?null;
? ? ? ? }
? ? ? ??
? ? ? ? ListNode(int?val, ListNode next) {
? ? ? ? ? ??this.val = val;
? ? ? ? ? ??this.next = next;
? ? ? ? }
? ? }
? ??
? ??// 迭代方式反转链表
? ??publicstatic?ListNode?reverseIteratively(ListNode head){
? ? ? ? ListNode prev =?null; ? ?// 前一个节点
? ? ? ? ListNode current = head;?// 当前节点
? ? ? ? ListNode next =?null; ? ?// 下一个节点
? ? ? ??
? ? ? ??while?(current !=?null) {
? ? ? ? ? ? next = current.next; ?// 保存下一个节点
? ? ? ? ? ? current.next = prev; ?// 反转当前节点的指针
? ? ? ? ? ? prev = current; ? ? ??// 移动prev到当前节点
? ? ? ? ? ? current = next; ? ? ??// 移动current到下一个节点
? ? ? ? }
? ? ? ??// 反转完成后,prev成为新的头节点
? ? ? ??return?prev;
? ? }
? ??
? ??// 递归方式反转链表
? ??publicstatic?ListNode?reverseRecursively(ListNode head){
? ? ? ??// 基准情况:空链表或只有一个节点,直接返回
? ? ? ??if?(head ==?null?|| head.next ==?null) {
? ? ? ? ? ??return?head;
? ? ? ? }
? ? ? ??
? ? ? ??// 递归反转剩余节点
? ? ? ? ListNode newHead = reverseRecursively(head.next);
? ? ? ??
? ? ? ??// 将当前节点的下一个节点的next指向当前节点
? ? ? ? head.next.next = head;
? ? ? ??// 断开当前节点的next指针,避免循环
? ? ? ? head.next =?null;
? ? ? ??
? ? ? ??// 返回新的头节点
? ? ? ??return?newHead;
? ? }
? ??
? ??// 打印链表
? ??publicstaticvoidprintList(ListNode head){
? ? ? ? ListNode current = head;
? ? ? ??while?(current !=?null) {
? ? ? ? ? ? System.out.print(current.val +?" ");
? ? ? ? ? ? current = current.next;
? ? ? ? }
? ? ? ? System.out.println();
? ? }
? ??
? ??publicstaticvoidmain(String[] args){
? ? ? ??// 创建一个示例链表: 1 -> 2 -> 3 -> 4 -> 5
? ? ? ? ListNode head =?new?ListNode(1);
? ? ? ? head.next =?new?ListNode(2);
? ? ? ? head.next.next =?new?ListNode(3);
? ? ? ? head.next.next.next =?new?ListNode(4);
? ? ? ? head.next.next.next.next =?new?ListNode(5);
? ? ? ??
? ? ? ? System.out.println("原始链表:");
? ? ? ? printList(head);
? ? ? ??
? ? ? ??// 迭代方式反转
? ? ? ? ListNode reversedByIteration = reverseIteratively(head);
? ? ? ? System.out.println("迭代法反转后:");
? ? ? ? printList(reversedByIteration);
? ? ? ??
? ? ? ??// 为了演示递归,重新创建链表
? ? ? ? head =?new?ListNode(1);
? ? ? ? head.next =?new?ListNode(2);
? ? ? ? head.next.next =?new?ListNode(3);
? ? ? ? head.next.next.next =?new?ListNode(4);
? ? ? ? head.next.next.next.next =?new?ListNode(5);
? ? ? ??
? ? ? ??// 递归方式反转
? ? ? ? ListNode reversedByRecursion = reverseRecursively(head);
? ? ? ? System.out.println("递归法反转后:");
? ? ? ? printList(reversedByRecursion);
? ? }
}

执行结果:

原始链表:
12345
迭代法反转后:
54321
递归法反转后:
54321

代码解析:

迭代法实现:使用三个指针prevcurrentnext,遍历链表时,逐个反转节点的指针方向。时间复杂度:O (n),需要遍历整个链表。空间复杂度:O (1),只使用了常数级别的额外空间。

递归法实现:递归的基准情况:空链表或只有一个节点时直接返回。递归处理剩余节点,然后将当前节点设置为下一个节点的后继。时间复杂度:O (n),需要访问每个节点一次。空间复杂度:O (n),递归调用栈的深度为链表长度

两种方法都能正确反转链表,迭代法通常效率更高(无递归调用开销),而递归法代码更简洁但可能在链表过长时导致栈溢出。

腾讯

腾讯

腾讯于1998年11月成立,是一家互联网公司,通过技术丰富互联网用户的生活,助力企业数字化升级。我们的使命是“用户为本 科技向善”。Founded in 1998, Tencent is an Internet-based platform company using technology to enrich the lives of Internet users and assist the digital upgrade of enterprises. Our mission is &quot;Value for Users, Tech for Good&quot;.

腾讯于1998年11月成立,是一家互联网公司,通过技术丰富互联网用户的生活,助力企业数字化升级。我们的使命是“用户为本 科技向善”。Founded in 1998, Tencent is an Internet-based platform company using technology to enrich the lives of Internet users and assist the digital upgrade of enterprises. Our mission is &quot;Value for Users, Tech for Good&quot;.收起

查看更多

相关推荐