面试记录

搜车的手写代码

面试需要手写一段死锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package SynchronizedLock;

/**
* Created by yqz on 3/14/18.
*/
public class MythreadA implements Runnable{
Object a;
Object b;
public MythreadA(Object a,Object b){
this.a=a;
this.b=b;
}
public void run() {
synchronized (a){
try {
System.out.println("线程A正在等待获取下个对象的锁,已经获取到对象锁:"+a);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (b){
System.out.println("线程A同时获取到对象A和B的锁");
}
}
}
}

触发线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
package SynchronizedLock;

/**
* Created by yqz on 3/14/18.
*/
public class SynchronizedLockMain {
public static void main(String[] args) {
Object a=new Object();
Object b=new Object();
new Thread(new MythreadA(a,b)).start();
new Thread(new MythreadA(b,a)).start();//调换一下ab顺序保证死锁发生的条件,否则不会发生死锁
}
}

2018-10-24 天猫笔试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//评测题目: 1.利用多线程有序打印(假定打印为IO阻塞耗时操作)出0~100的所有自然数(使用线程池编码)
2.利用数组实现栈的push和pop操作,以及min函数,得到栈的最小元素;
时间:40分钟
说明:如API记不清楚可使用伪代码示意既可
Class ThreadFirst implement Runable{
private int from;
private int end;
ThreadPrint(int from ,int end){

}
@Override
void run(){
//先分三个线程打印吧
while(PrintData.count%3 == 0){
//第一次打印的线程
printData(from,end);
count++;
break;
}
}
}
Class ThreadSecond implement Runable{
private int from;
private int end;
ThreadPrint(int from ,int end){

}
@Override
void run(){
//先分三个线程打印吧
while(PrintData.count%3 == 1){
//第一次打印的线程
printData(from,end);
count++;
break;
}
}
}
Class ThreadThird implement Runable{
private int from;
private int end;
ThreadPrint(int from ,int end){

}
@Override
void run(){
//先分三个线程打印吧
while(PrintData.count%3 == 2){
//第一次打印的线程
printData(from,end);
count++;
break;
}
}
}
Class PrintData{
public static volatile int count=0;
public static void main(String[] args){
new ThreadFirst(0,30).start();
new ThreadFirst(31,60).start();
new ThreadFirst(60,100).start();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//获取一个最小的栈值

public class MinStack{

private List<Integer> data=new ArrayList<Integer>{};

private List<Integer> minIntegers= new ArrayList<Integer>{};

public void push(int num){

data.add(num);

if(minIntegers.size() ==0){
minIntegers.add(0);
}else{
//保存一下最小值的索引
int min =minOfStack();
if(num < min){
//如果在push的时候找到了更小的值保存到最小值的列表中
minIntegers.add(data.size()-1);
}
}
}
public void pop(){
//如果data pop完抛出异常
if(data.size()==0){
throw new Exception("栈已经没有元素");
}

//在pop的时候需要同步pop一下最小列表里面的索引值,什么时候出栈?
int popIndex=data.size() -1;

int minIndex = minIntegers.get(minIntegers.size()-1);

if(popIndex == minIndex){
//粗zhan
minIntegers.remove(minIntegers.size()-1);
}
}

public int minOfStack(){
//判空
if(data.size()==0){
throw new Exception("zhan元素异常");
}
//每次取zhan顶的元素
int minIndex = minIntegers.get(minIntegers.size-1);

return data.get(minIndex);
}

}
蚂蚁金服面试两个线程交替打印1-100的整数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.souche.study.PrintData;


/**
* 这道题可以通过等待通知(wait/notify +synchronized通知模型)机制或者volatile关键字去控制线程的执行顺序
* 本质上是利用java共享内存模型进行通信
* 这里先给一种循环cas的解法
* Created by yqz on 12/18/18.
*/
public class PrintData {
private static String str="alipay";
//定义一个线程数
public static volatile int threadCount=2;
//记录需要打印的次数
public static volatile int printCount=100;
public static void main(String[] args) {
new Thread(new ThreadA()).start();
new Thread(new ThreadB()).start();

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.souche.study.PrintData;

/**
* Created by yqz on 12/18/18.
*/
public class ThreadA implements Runnable {

@Override
public void run() {
//无锁,循环cas控制
while(true) {
if (PrintData.threadCount % 2 == 0 ){
//sout
System.out.println(Thread.currentThread().getName()+":"+(101-PrintData.printCount));
PrintData.threadCount--;
PrintData.printCount--;
}
if(PrintData.printCount==1){
break;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.souche.study.PrintData;

/**
* Created by yqz on 12/18/18.
*/
public class ThreadB implements Runnable {

@Override
public void run() {
//通过volatile控制
while(true) {
if (PrintData.threadCount % 2 == 1 ){
//sout
System.out.println(Thread.currentThread().getName()+":"+(101-PrintData.printCount));
//这里重新加回去volatile可见性保证通知到A线程
PrintData.threadCount++;
//打印了一次之后减少一次
PrintData.printCount--;
}
if(PrintData.printCount==2){
break;
}
}
}
}
字符串反转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.souche.study.PrintABC;

import org.springframework.util.StringUtils;

/**
* 先写的第二道,第一道没时间,暂时写个方式,那里的判断好像没有什么必要注释掉
* Created by yqz on 12/18/18.
*/
public class Reverse {
private static String str="alipayy";
public static void main(String[] args) {
try {
reverse(str);
}catch (Exception e){
//logger
}

}
private static String reverse(String str) throws Exception {
if(StringUtils.isEmpty(str)){
throw new Exception("字符串不能为空");
}
char[] chars=str.toCharArray();
//可以取一下中间的位置,然后两边互换一下位置,但是数组不好换位置,需要一个中间的变量进行轮转
int length =str.length();
int midIndex=length/2;
int canquyu=length%2;
/*if(canquyu==0){//偶数长度都要互换
for(int i=0;i<midIndex;i++){
char mid =chars[i];
chars[i]=chars[length-i-1];//执行赋值的过程
chars[length-i-1]=mid;
}
}else{//基数中间的不用还
for(int i=0;i<midIndex;i++){
char mid =chars[i];
chars[i]=chars[length-i-1];//执行赋值的过程
chars[length-i-1]=mid;
}
}*/
for(int i=0;i<midIndex;i++){
char mid =chars[i];
chars[i]=chars[length-i-1];//执行赋值的过程
chars[length-i-1]=mid;
}
for(int i=0;i<length;i++){
System.out.print(chars[i]);
}
return new String(chars);
}
}

如何快速定位线上cpu飙升(linux环境)

1
2
3
4
5
6
7
8
//1. 通过top命令查看到cpu占用最高的pid 这个pid是processid
top
//2. 通过top -p <pid> -H命令查看当前进程下哪个线程占用了最多的cpu,记录threadid
top -p <pid> -H
//3. 第二步获取到的threadid默认是十进制,需要转换成十六进制进行处理
printf "%x" threadid
//4. 通过jstack定位到具体的线程,通过线程的状态记录跟踪到代码的具体位置
jstack <pid> | grep threadid

大文件排序

内存有限的情况下,进行大文件排序,基本思想是归并排序。这里记录一下思维漏洞,在进行分治之后,每个小文件都是有序的,这里怎么对小文件进行合并?之前想到这个地方经常会陷入思维误区,想把小文件如何在内存中进行比较,或者如何定位到文件的具体位置,如果写到文件位置的缝隙中?记录一下这个思维误区。其实可以通过例如我们将文件分成了三份,三份文件都排好序:

1
2
3
4
5
6
-- 文件1 有序 数字 1 3 7
1 3 7
-- 文件2 有序 数字 2 4 5
2 4 5
-- 文件3 有序 数字 6 8 9
6 8 9

这里把每个文件的最小值先加载到内存,内存中就是三个数值的数组 a[3](0 1 2分别保存当前每个文件队列的当前最小值,当前最小值判定为所有文件最小值之后,将最小值pop之后继续读本文件下一行的最小值)。这里可以假设内存只能容下三个数字,每个利用readline一行一行往下读。

分布式事务如何保证一致性

面试记录,曾经在公司内部topic中讨论过这个主题,面试的时候竟然忘了,这里列举几种保证分布式事务一致性的方案。

两阶段提交/XA方案

Spring+JTA两阶段提交保证一致性,这里有一个事务管理器的概念,