Java常见场景题
1、如果一个外卖配送单子要发布,现在有200个骑手都想要接这一单,如何保证只有一个骑手接到单子?
如果只是单机,采用volatile关键字修饰该订单采用CAS操作对其进行乐观锁操作。
采用redis,zookeeper分布式锁加锁。
消息队列 实现幂等接口
2、多个微信用户抢红包
这个和上题一样的道理
3、美团首页每天会从 10000 个商家里面推荐 50 个商家置顶,每个商家有一个权值,你如何来推荐?第二天怎么更新推荐的商家?
可以借鉴下 stackoverflow,视频网站等等的推荐算法。
4、1000个任务分给10个人做
- hash
- 创建10个线程,然后用同步锁,Synchornized
5、保证发送消息的有序性,消息处理的有序性。
6、如何把一个文件快速下发到 100w 个服务器
采用p2p网络形式 比如树状形式,网状形式 单个节点既可以从其他节点接收服务又可以向其他节点提供服务。
对于树状传递,在100W台服务器这种量级上,可能存在两个问题
- 如果树上的某一个节点坏掉了,那么从这个节点往下的所有服务器全部宕机。
- 如果树中的某条路径,传递时间太长了(网络中,两个节点间的传递速度受很多因素的影响,可能相差成百上千倍),使得传递效率退化。
改进:100W台服务器相当于有100W个节点的连通图。那么我们可以在图里生成多颗不同的生成树,在进行数据下发时,同时按照多颗不同的树去传递数据。这样就可以避免某个中间节点宕机,影响到后续的节点。同时这种传递方法实际上是一种依据时间的广度优先遍历,可以避免某条路径过长造成的效率低下。
7、给每个组分配不同的 IP 段,怎么设计一种结构使的快速得知 IP 是哪个组的?
难道不是划分子网,添加子网掩码
8、10 亿个数,找出最大的 10 个。
(1)单机+单核+足够大内存
维护一个小顶堆 每一次都与顶堆即最小的数进行比较。如果某一后续元素比容器内最小数字大,则删掉容器内最小元素,并将该元素插入容器
(2)单机+多核+足够大内存
这时可以直接在内存总使用Hash方法将数据划分成n个partition,每个partition交给一个线程处理,线程的处理逻辑同(1)类似,最后一个线程将结果归并。
该方法存在一个瓶颈会明显影响效率,即数据倾斜。每个线程的处理速度可能不同,快的线程需要等待慢的线程,最终的处理速度取决于慢的线程。而针对此问题,解决的方法是,将数据划分成c×n个partition(c>1),每个线程处理完当前partition后主动取下一个partition继续处理,知道所有数据处理完毕,最后由一个线程进行归并。
9、有几台机器存储着几亿淘宝搜索日志,你只有一台 2g 的电脑,怎么选出搜索热度最高的十个
可以拆分成n多个文件:以首字母区分,不同首字母放在不同文件,长度仍过长的继续按照次首字母进行拆分
这样一来,每个文件的每个数据长度相同且首字母尾字母也相同,就能保证数据被独立的分为了n个文件,且各个文件中不存在关键词的交集。
对于每个文件,使用hash或者Trie树进行进行词频统计并用大根堆获取词频最多的10个词。依次处理每个文件,并逐渐更新最大的十个词
10、分布式集群中如何保证线程安全?
11、给个淘宝场景,怎么设计一消息队列?
https://blog.csdn.net/gv7lzb0y87u7c/article/details/91443605 和 https://www.jianshu.com/p/3ebde7d36460
12、10 万个数,输出从小到大?
- 多路归并排序算法 在N个数中取M个数排序后放到内存中,然后多路归并算法进行合并
- 采用位图标志该数量的个数 标志完成后再遍历bitmap进行排序
13、有十万个单词,找出重复次数最高十个?
针对top k类问题,通常比较好的方案是【分治+trie树/hash+小顶堆】,即先将数据集按照hash方法分解成多个小数据集,然后使用trie树或者hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出频率最高的前K个数,最后在所有top K中求出最终的top K。类似上面第8题
14、在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数
位操作。采用bitmap方法来对每一个数据进行标记。0代表存在,1代表不存在。
15、两个线程交替打印
第一种方法:加锁,并使用volatile boolean类型的标志来控制线程执行
public class sub2{
static volatile int num=1;
static volatile boolean flag=true;
public static void main(String[] args) {
Object obj=new Object();
new Thread(()->{
while(num<=100){
if(flag){
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
flag=false;
}
}
},"A").start();
new Thread(()->{
while(num<=100){
if(!flag){
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
flag=true;
}
}
},"B").start();
}
}
第二种方法:加锁,一个线程执行完就等待并唤醒另一个线程
public class sub2{
static volatile int num=1;
public static void main(String[] args) {
Object obj=new Object();
new Thread(()->{
while(num<=100){
synchronized (obj){
obj.notifyAll();
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"A").start();
new Thread(()->{
while(num<=100) {
synchronized (obj){
obj.notifyAll();
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"B").start();
}
}