百度今年的薪资,诚意满满。。

hello,大家好,我是千羽。
百度去年开的比较早,在10月份就开始了。今年晚了两个月
逛了一下牛客网发现25届同学拿28k,26k还不少再来对比一下去年24届校招offer的情况:
百度年总包构成 = 月薪 x 16

评级
薪资
年包
百度
普通 offer
22k16 35w 百度 sp offer 24k~25k16
38w~40w
百度
sss offer
29k~32k*16+签字费6w/2年
46w~54w
24届百度的签字费好像是 ssp offer 才有,总共是 6w,分 2 年给,第一年能拿到 3w。
在11月份的25届校招offer基本上都开奖了,没上岸的也没关系,后续也会有补录~

评级
薪资
年包
百度
普通 offer
24k * 16 + 6w 签字费
40w
百度
sp offer
26k ~29k* 16 + 6w 签字费
45w
百度
sss offer
32k * 16 + 12w 签字费
48w
好了,再来看看今年百度一面Java面经。面试不难,很常见的八股文,建议还是要多背一下八股了。
java多线程怎么等待其他线程的结果
方法一:使用Thread.join()
适用场景:当需要等待一个或多个线程执行完毕,再继续执行主线程中的逻辑时。
示例代码
public class JoinExample {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println(“Thread 1 finished.”);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});

    Thread thread2 = new Thread(() -> {
        try {
            Thread.sleep(2000);
            System.out.println("Thread 2 finished.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });

    thread1.start();
    thread2.start();

    thread1.join(); // 主线程等待 thread1 完成
    thread2.join(); // 主线程等待 thread2 完成

    System.out.println("All threads finished.");
}

}
输出
Thread 1 finished.
Thread 2 finished.
All threads finished.
方法二:使用CountDownLatch
适用场景:当有多个线程需要完成任务,然后通知主线程继续执行。
示例代码
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int threadCount = 2;
CountDownLatch latch = new CountDownLatch(threadCount);

    Thread thread1 = new Thread(() -> {
        try {
            Thread.sleep(1000);
            System.out.println("Thread 1 finished.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            latch.countDown(); // 递减计数
        }
    });

    Thread thread2 = new Thread(() -> {
        try {
            Thread.sleep(2000);
            System.out.println("Thread 2 finished.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            latch.countDown(); // 递减计数
        }
    });

    thread1.start();
    thread2.start();

    latch.await(); // 等待计数器归零
    System.out.println("All threads finished.");
}

}
输出
Thread 1 finished.
Thread 2 finished.
All threads finished.
总结
Thread.join():简单直接,适用于少量线程。
CountDownLatch:用于多个线程的同步等待。
redis的lua脚本原理
(1)脚本的发送与存储
客户端通过EVAL 或EVALSHA 命令发送 Lua 脚本到 Redis。
Redis 使用内置的 Lua 解释器(基于Lua 5.1)执行脚本。
脚本的内容会被 Redis 缓存,如果脚本未修改,可以通过EVALSHA 直接使用脚本的 SHA1 校验值调用,避免再次传输脚本内容。
(2)执行环境
Redis 会为 Lua 脚本提供一个安全的执行环境,脚本运行时无法直接访问外部资源(如文件、网络),只能通过 Redis 提供的 API 与数据交互。
脚本中的 Redis 命令通过
redis.call

redis.pcall
执行:
redis.call:普通调用,出现错误会中断脚本。
redis.pcall:安全调用,出现错误不会中断脚本,而是返回错误信息。
(3)原子性保证
在脚本执行期间,Redis 会阻塞其他命令的执行,从而确保脚本的所有操作是一个完整的事务。
即使脚本中调用了多个命令,这些命令也会以原子方式执行。
(4)结果返回
Lua 脚本执行的返回值会被 Redis 转换成客户端可识别的格式,如字符串、数组等。
常见场景
(1)分布式锁
使用 Lua 脚本实现加锁和解锁的原子性:
if redis.call(“setnx”, KEYS[1], ARGV[1]) == 1 then
redis.call(“expire”, KEYS[1], ARGV[2])
return true
else
return false
end
(2)计数器
如实现限流器:
current = redis.call(“incr”, KEYS[1])
if current == 1 then
redis.call(“expire”, KEYS[1], ARGV[1])
end
if current > tonumber(ARGV[2]) then
return false
else
return true
end
(3)批量操作
批量删除匹配的键:
local keys = redis.call(‘keys’, ARGV[1])
for i, key in ipairs(keys) do
redis.call(‘del’, key)
end
return keys
redis单线程的理解
Redis 的单线程模型指的是网络 I/O 和命令处理 都由主线程完成。即:
一个线程负责接受请求、解析命令、执行操作、返回结果。
同一时刻,Redis 只处理一个客户端的命令。
redis分布式锁原理,具体细节,可重入怎么实现
Redis 分布式锁的具体实现
加锁
示例代码:
function acquireLock($redis, $lockKey, $lockValue, $expireTime) {
// 尝试加锁
return $redis->set($lockKey, $lockValue, [‘NX’, ‘PX’ => $expireTime]);
}
释放锁
使用 Lua 脚本原子释放锁:
function releaseLock($redis, $lockKey, $lockValue) {
$script = ‘
if redis.call(“GET”, KEYS[1]) == ARGV[1] then
return redis.call(“DEL”, KEYS[1])
else
return 0
end’;
return $redis->eval($script, [$lockKey, $lockValue], 1);
}
锁续期
通过一个后台线程延长锁的过期时间:
function extendLock($redis, $lockKey, $lockValue, $newExpireTime) {
$script = ‘
if redis.call(“GET”, KEYS[1]) == ARGV[1] then
return redis.call(“PEXPIRE”, KEYS[1], ARGV[2])
else
return 0
end’;
return $redis->eval($script, [$lockKey, $lockValue, $newExpireTime], 1);
}
可重入锁的实现
原理
可重入锁允许同一线程多次获取同一把锁,但需要记录加锁次数,并在解锁时递减计数。当计数归零时释放锁。
import redis.clients.jedis.Jedis;

public class ReentrantRedisLock {
private final Jedis jedis;
private final String lockKey;
private final String lockValue;
private final int expireTime;

public ReentrantRedisLock(Jedis jedis, String lockKey, String lockValue, int expireTime) {
    this.jedis = jedis;
    this.lockKey = lockKey;
    this.lockValue = lockValue;
    this.expireTime = expireTime;
}

/**
 * 尝试获取锁
 */
public boolean acquireLock() {
    if (lockHeldByCurrentThread()) {
        jedis.hincrBy(lockKey, "count", 1);
        return true;
    }

    String result = jedis.set(lockKey, lockValue, "NX", "PX", expireTime);
    if ("OK".equals(result)) {
        jedis.hset(lockKey, "count", "1");
        return true;
    }
    return false;
}

/**
 * 判断当前线程是否持有锁
 */
private boolean lockHeldByCurrentThread() {
    String value = jedis.hget(lockKey, "owner");
    return lockValue.equals(value);
}

/**
 * 释放锁
 */
public boolean releaseLock() {
    if (!lockHeldByCurrentThread()) {
        return false;
    }

    long count = jedis.hincrBy(lockKey, "count", -1);
    if (count == 0) {
        jedis.del(lockKey);
        return true;
    }
    return false;
}

}
MySQL 索引失效的原因与隐式转换
在 MySQL 中,索引是提高查询性能的关键。但是某些情况下,索引可能无法生效,导致全表扫描。以下是索引失效的常见原因,以及隐式转换对索引的影响。

  1. 索引失效的常见原因
    1.1 查询条件使用了函数
    如果查询条件对索引字段使用了函数或表达式,MySQL 无法利用索引。
    示例:
    — 索引字段 name
    SELECT * FROM users WHERE UPPER(name) = ‘JOHN’; — 索引失效
    解决办法: 尽量避免对索引字段使用函数或表达式,将处理逻辑移到查询条件外部。
    — 转为小写比较
    SELECT * FROM users WHERE name = LOWER(‘JOHN’);
    1.2 查询条件中使用了!=或<>
    != 或<> 会导致 MySQL 放弃使用索引,改为全表扫描。
    示例:
    SELECT * FROM users WHERE age != 30; — 索引失效
    解决办法: 尝试改用其他查询逻辑,如范围查询。
    1.3 查询条件中使用OR
    如果OR 两侧的条件中有一个未使用索引,则索引失效。
    示例:
    SELECT * FROM users WHERE age = 25 OR name = ‘John’; — 索引失效
    解决办法: 用UNION 将查询拆分,分别利用索引优化。
    SELECT * FROM users WHERE age = 25
    UNION
    SELECT * FROM users WHERE name = ‘John’;
    1.4 使用了前导通配符
    在LIKE 查询中,如果使用前导通配符%,索引无法生效。
    示例:
    SELECT * FROM users WHERE name LIKE ‘%John’; — 索引失效
    解决办法: 避免使用前导通配符,或使用全文索引。
    SELECT * FROM users WHERE name LIKE ‘John%’;
    1.5 隐式转换导致索引失效
    当查询条件的数据类型与索引字段不一致时,MySQL 会发生隐式类型转换,这会导致索引失效。
  2. 隐式转换的影响
    2.1 什么是隐式转换?
    MySQL 在查询时,如果字段类型与查询条件的类型不一致,会自动进行类型转换。例如:
    字符串转数字
    时间转字符串
    2.2 隐式转换导致索引失效的场景
    场景 1:字符串和数字的比较
    示例:
    — 假设 id 是字符串类型
    SELECT * FROM users WHERE id = 123; — 索引失效
    MySQL 将把索引字段id 转换为数字进行比较,这会导致全表扫描。
    解决办法: 确保查询条件与字段类型一致。
    SELECT * FROM users WHERE id = ‘123’; — 索引生效
    场景 2:日期与字符串比较
    示例:
    — 假设 created_at 是 DATE 类型
    SELECT * FROM orders WHERE created_at = ‘2024-11-29’; — 索引失效
    MySQL 会将created_at 转换为字符串进行比较,导致索引失效。
    解决办法: 确保条件值与字段类型一致。
    SELECT * FROM orders WHERE created_at = DATE(‘2024-11-29’); — 索引生效
    场景 3:字段类型不一致
    示例:
    — 假设 age 是 INT 类型
    SELECT * FROM users WHERE age = ’30’; — 索引失效
    MySQL 会将age 转换为字符串类型,导致全表扫描。
    解决办法: 保证查询值的类型与字段一致。
    SELECT * FROM users WHERE age = 30; — 索引生效
    如何检查隐式转换?
    通过EXPLAIN 分析查询执行计划,查看是否使用了索引。
    示例:
    EXPLAIN SELECT * FROM users WHERE age = ’30’;
    在Extra 字段中,如果出现Using where 或Using filesort,则索引可能失效。
  3. 总结
    索引失效原因 解决办法
    对索引字段使用函数或表达式
    将处理逻辑移到查询条件外部
    使用!= 或<>
    改用范围查询或重新设计查询逻辑
    使用OR
    使用UNION 将查询拆分
    前导通配符%
    改用全文索引或后缀通配符
    隐式类型转换
    确保查询条件与字段类型一致
    避免索引失效的关键在于优化查询语句,避免 MySQL 进行额外的类型转换或函数计算。通过EXPLAIN 分析执行计划,可以快速发现问题所在并优化查询逻辑。
    mysql唯一索引和聚簇索引的区别
    唯一索引是一种限制,用于保证指定的一列或多列的值唯一。
    而聚簇索引是一种物理存储方式,用于按照一定的顺序和范围进行访问。
    MySQL中使用聚簇索引来实现主键,并且使用聚簇索引来存储表中的数据。
    如果不存在主键,则会使用第一个非空唯一索引作为聚簇索引,如果也不存在非空唯一索引,则会使用一个隐藏的ROWID作为聚簇索引。
    mysql联合索引的原理
  4. 什么是联合索引
    联合索引(Composite Index)是指在 MySQL 中对多个列创建的索引。与单列索引不同,联合索引包含多个列的排序信息,可以同时加速这些列的查询。
    创建联合索引示例:
    CREATE INDEX idx_user_name_age ON users(name, age);
    该索引包含了name 和age 两列的信息,可以提升针对这两个列的特定查询性能。
  5. 联合索引的底层结构
    MySQL 的联合索引通常基于B+树 数据结构。在联合索引中,多个列的值被组合成一个整体进行排序。
    例如,索引(name, age) 的排序规则:
    按照name 列排序;
    如果name 相同,则再按照age 列排序。
    数据排列示例:
    (name, age) (‘Alice’, 20) (‘Alice’, 25) (‘Bob’, 30) (‘Charlie’, 22)
  6. 最左前缀原则
    联合索引的查询加速依赖于最左前缀原则,即索引只能用于从最左边的列开始的查询。
    规则:
    查询条件必须包含索引的最左列。
    一旦跳过索引中的某列,后续列的索引失效。
    示例: 索引(name, age, city) 的适用范围:
    可以使用索引:
    name
    name, age
    name, age, city
    无法使用索引:
    age (跳过了name)
    city (跳过了name 和age)
  7. 联合索引的作用
    联合索引既可以加速单列查询,也可以加速多列联合查询。
    单列查询: 如果查询只包含索引中的第一列,联合索引可以像单列索引一样使用。
    SELECT * FROM users WHERE name = ‘Alice’;
    多列查询: 如果查询包含索引中的多列,MySQL 会利用索引中已排序的顺序来快速定位记录。
    SELECT * FROM users WHERE name = ‘Alice’ AND age = 25;
  8. 索引匹配的具体场景
    以下是对(name, age, city) 联合索引的各种查询情况及其索引使用情况:
    查询语句 索引使用情况 原因
    SELECT * FROM users WHERE name = ‘Alice’;
    使用索引(name)
    匹配最左列。
    SELECT * FROM users WHERE name = ‘Alice’ AND age = 25;
    使用索引(name, age)
    匹配最左前缀(name, age)。
    SELECT * FROM users WHERE name = ‘Alice’ AND city = ‘NY’;
    使用索引(name)
    跳过age 后,city 部分无法使用索引。
    SELECT * FROM users WHERE age = 25;
    无法使用索引
    缺少最左列name。
    SELECT * FROM users WHERE city = ‘NY’;
    无法使用索引
    缺少最左列name 和中间列age。
    怎么解决i++的线程安全问题
    在多线程环境中,i++ 并不是线程安全的操作,因为它包含以下三个步骤:
    读取变量i 的值。
    对变量i 的值加 1。
    将结果写回变量i。
    由于这三个步骤不是原子性的,不同线程可能在操作之间发生竞争,从而导致数据不一致的问题。
    使用线程本地变量(ThreadLocal)
    public class ThreadSafeIncrement {
    private final ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 0); public void increment() {
    threadLocal.set(threadLocal.get() + 1);
    } public int getValue() {
    return threadLocal.get();
    }
    }
    手写算法:重排链表
    重排链表是一个常见的链表操作问题,其目标是对链表的节点进行重新排序,使其按照以下顺序排列:
    L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
    解决思路
    找到链表的中点:通过快慢指针找到链表的中间节点。
    反转链表的后半部分:从中点开始,反转后半部分链表。
    合并两部分链表:将前半部分和后半部分链表按照要求交叉合并。
    class ListNode {
    int val;
    ListNode next;
    ListNode(int val) {
    this.val = val;
    this.next = null;
    }
    }

public class ReorderList {
public void reorderList(ListNode head) {
if (head == null || head.next == null) {
return; // 链表为空或只有一个节点,无需重排
}

    // Step 1: 使用快慢指针找到链表中点
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }

    // Step 2: 反转链表后半部分
    ListNode secondHalf = reverseList(slow.next);
    slow.next = null; // 切断前半部分和后半部分的连接

    // Step 3: 合并两个链表
    ListNode firstHalf = head;
    while (secondHalf != null) {
        ListNode temp1 = firstHalf.next;
        ListNode temp2 = secondHalf.next;

        firstHalf.next = secondHalf;
        secondHalf.next = temp1;

        firstHalf = temp1;
        secondHalf = temp2;
    }
}

// 辅助函数:反转链表
private ListNode reverseList(ListNode head) {
    ListNode prev = null, curr = head;
    while (curr != null) {
        ListNode nextTemp = curr.next;
        curr.next = prev;
        prev = curr;
        curr = nextTemp;
    }
    return prev;
}

// 打印链表
public void printList(ListNode head) {
    while (head != null) {
        System.out.print(head.val + " -> ");
        head = head.next;
    }
    System.out.println("null");
}

// 测试
public static void main(String[] args) {
    ReorderList rl = new ReorderList();

    // 构造链表 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("原链表:");
    rl.printList(head);

    rl.reorderList(head);

    System.out.println("重排后的链表:");
    rl.printList(head);
}

}
代码说明
找到中点:使用快慢指针,慢指针每次移动一步,快指针每次移动两步。最终慢指针会停在链表的中间位置。
反转后半部分链表:遍历链表并将节点指针指向其前一个节点。
合并链表:按照L0 → Ln → L1 → Ln-1 的顺序交叉连接两个链表的节点。
测试结果
输入链表:
1 -> 2 -> 3 -> 4 -> 5
输出链表:
1 -> 5 -> 2 -> 4 -> 3 -> null
复杂度分析
时间复杂度:
找到中点:O(n)
反转后半部分链表:O(n)
合并两个链表:O(n)
总时间复杂度:O(n)
空间复杂度:
只使用了常数级的额外空间,空间复杂度:O(1)
参考文献:https://www.nowcoder.com/feed/main/detail/bed654364b224527bbdc46978edd00dc?sourceSSR=search

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/424030.html

联系我们
联系我们
分享本页
返回顶部