字节跳动秋招面试后台岗位

笔试5个题目,比较幸运一次过了。大概做了2.5的样子。

一面:40分钟左右

编程题目:

1.给出一个有序数组的翻转数组,如何求到原数组的中位数

剑指offer上有个题目是给出有序数组的翻转数组,然后求其最小值。可以使用二分的方式解。

此题我当时给出的答案是

a.找到翻转点,然后还原原数组,自然求得中位数。时间复杂度o(n) ,空间复杂度o(n),在这个思路上可以进行优化,可以不使用额外的空间。

b.可以用排序算法求出原数组,时间复杂度o(nlogn),但是没用到翻转数组的特性

c.由于其实问题就是求第k个位置的元素是多少,可以借助快排的partition来辅助,期望时间复杂度o(n)。同样没用到翻转数组的特性。

2.写一个线程安全的单例模式

https://blog.csdn.net/u011010851/article/details/79873058

饿汉式:因为很饿,所以一开始就把对象new好了。对象,构造函数私有。提供public的get方法。是线程安全的。

懒汉式:有人调用的时候才new。对象,构造函数私有。提供public的get方法。线程非安全,需要把get方法定义成sychronized或者使用同步代码块(双检锁)。

面试中未回答的好的问题

1.http报文信息

https://www.cnblogs.com/jiu0821/p/5641600.html

请求行/请求头/空白行/请求体

2.进程调度都有哪些算法

https://www.cnblogs.com/Blog-day/p/My_Blog_Days1-11.html

先来先服务;短作业优先;高优先级先执行;高响应比优先;时间片轮转;多级反馈队列调度

二面:40分钟左右

编程题目:

1.单链表的原地逆序

a.采用递归的方式写

 public static ListNode reorder1(ListNode root){
        if (root == null){
            return null;
        }else if (root.next == null){
            return root;
        }
        ListNode next = reorder(root.next);
        root.next.next = root;
        root.next = null;
        return next;
    }

b.采用指针的方式进行处理

public static ListNode reorder(ListNode root){
        if (root == null){
            return null;
        }
        ListNode pre = null;
        ListNode now = root;
        ListNode next = root.next;
        while (next!=null){
            now.next = pre;
            pre = now;
            now = next;
            next = next.next;
        }
        now.next = pre;
        return now;
    }

c.非原地的方法就是借助栈

面试中未回答的好的问题

1.索引应该建立在怎样的列上

https://blog.csdn.net/ahhsxy/article/details/5458629?utm_source=jiancool

不应该建立索引的列:在查询中比较少使用;只有少量数据值;定义为text,image,bit的列不应该使用;修改的性能远远大于检索的性能;

2.四种引用类型

http://www.cnblogs.com/yaowen/p/6292984.html

强引用:当我们使用new创建对象时,被创建的对象就是强引用,如Object object = new Object(),其中的object就是一个强引用了。如果一个对象具有强引用,JVM就不会去GC它,JVM宁可会报OOM来终止程序,也不回收该对象。 

软引用: 如果一个对象只具备软引用,如果内存空间足够,那么JVM就不会GC它,如果内存空间不足了,就会GC该对象。 

弱引用: 如果一个对象只具有弱引用,只要JVM的GC线程检测到了,就会立即回收。弱引用的生命周期要比软引用短很多。不过,如果垃圾回收器是一个优先级很低的线程,也不一定会很快就会释放掉软引用的内存。 

虚引用:如果一个对象只具有虚引用,那么它就和没有任何引用一样,随时会被JVM当作垃圾进行GC。 

3.数据库的4种隔离级别

https://www.cnblogs.com/huanongying/p/7021555.html

首先要解释脏读,不可重复读,幻读

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)MMUC
可重复读(repeatable-read)MMUC
串行化(serializable)

4.信号量机制

https://blog.csdn.net/lierming__/article/details/78986679

通过pv操作来进行互斥和同步。有类似整形信号量,记录型信号量等;

5.路由器分段和重组

https://blog.csdn.net/hanzhen7541/article/details/79031781

对于不同的网络,其中传送的包大小可能不一样,因此把大包分小的功能是必须的。分段是将数据分组分割成小块以便它们能够适合基础网络的帧。数据报也可以被标记为“不可分段”,如果一个数据报被标记了,那么在任何情况下都不准对它进行分段。如果不分段到不了目的地,那就把包在半路抛弃了。在本地网内进行的重新分段和重组对IP模块是不可见的,这种方法也可以使用。

透明分段原则是:当包遇到了通不过的子网的时候,在进入之前由路由器按照子网的MTU(最大传输单元)进行分段,前面的分段对于后面的网络透明,离开子网的时候重组经过分段的包。

不透明分段:在任何中间网关都不进行重组,必要的时候只进行分段,仅仅在目标主机进行重组。

3面:3面问的非常专业和细节,很有难度,对我很有帮助,很多问题会开放性的问下如果是你会如何设计(然而并答不好)

编程题:

题目很简单,但是提到了两个细节问题,不专业的写出来的代码虽然功能Ok,但是面试官一问我发现以前有很多我没考虑到的问题。

1.报错机制应该如何设计

a.通过返回值来通知用户是否出错。这种方法会导致无法直接返回返回值。

b.发生错误时设置一个全局变量,则函数可以直接传递返回值;然后设置一个函数去分析这个全局变量。存在的问题就是经常忘记检查全局变量。

c.异常:在发生错误时抛出异常,并且可以根据错误的原因抛出对应的异常

2.实例化类中资源时,应该写在什么地方

由于在实例化类中资源时为了防止有报错的情况,所以最好放在构造函数中

面试中未回答的好的问题

1.java中对锁的优化

https://blog.csdn.net/wodewutai17quiet/article/details/78187386

锁消除:java在编译时通过上下文扫描去除某些不可能出现共享资源竞争的锁

eg:StringBuffer的append方法是一个同步方法,如果StringBuffer类型的变量是一个局部变量,则该变量就不会被其它线程所使用,即对局部变量的操作是不会发生线程不安全的问题。在这种情景下,JVM会在JIT编译时自动将append方法上的锁去掉。

锁粗化:将多个连续的加锁和释放操作合并成一个大的锁

eg:举例:在for循环里的加锁/解锁操作,一般需要放到for循环外

无锁/偏向锁/轻量级锁/重量级锁:锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁、轻量级锁、重量级锁。锁的状态会随着竞争情况逐渐升级,并且只可以升级而不能降级。

偏向锁:

背景:大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。

概念:核心思想就是锁会偏向第一个获取它的线程,如果在接下来的执行过程中没有其它的线程获取该锁,则持有偏向锁的线程永远不需要同步。

目的:偏向锁实际上是一种优化锁,其目的是为了减少数据在无竞争情况下的性能损耗。

原理:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID。以后该线程在进入和退出同步块时就不需要进行CAS操作来加锁和解锁,只需简单地判断一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

偏向锁的获取:

访问Mark Word中偏向锁的标识位是否为1,如果是1,则确定为偏向锁。如果偏向锁的标识位为0,说明此时是处于无锁状态,则当前线程通过CAS操作尝试获取偏向锁,如果获取锁成功,则将Mark Word中的偏向线程ID设置为当前线程ID;并且将偏向标识位设为1。如果偏向锁的标识位不为1,也不为0(此时偏向锁的标识位没有值),说明发生了竞争,偏向锁已经膨胀为轻量级锁,这时使用CAS操作尝试获得锁。如果是偏向锁,则判断Mark Word中的偏向线程ID是否指向当前线程,如果偏向线程ID指向当前线程,则表明当前线程已经获取到了锁;如果偏向线程ID并未指向当前线程,则通过CAS操作尝试获取偏向锁,如果获取锁成功,则将Mark Word中的偏向线程ID设置为当前线程ID;如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点时(在这个时间点上没有正在执行的字节码),获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。

偏向锁的释放:

当其它的线程尝试获取偏向锁时,持有偏向锁的线程才会释放偏向锁。

释放偏向锁需要等待全局安全点(在这个时间点上没有正在执行的字节码)。过程:首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态,如果线程还活着,说明此时发生了竞争,则偏向锁升级为轻量级锁,然后刚刚被暂停的线程会继续往下执行同步代码。

优点:加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距

轻量级锁:(自旋)

当使用轻量级锁(锁标识位为00)时,线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中(注:锁记录中的标识字段称为Displaced Mark Word)。

将对象头中的MarkWord复制到栈桢中的锁记录中之后,虚拟机将尝试使用CAS将对象头中Mark Word替换为指向该线程虚拟机栈中锁记录的指针,此时如果没有线程占有锁或者没有线程竞争锁,则当前线程成功获取到锁,然后执行同步块中的代码。

如果在获取到锁的线程执行同步代码的过程中,另一个线程也完成了栈桢中锁记录的创建,并且已经将对象头中的MarkWord复制到了自己的锁记录中,然后尝试使用CAS将对象头中的MarkWord修改为指向自己的锁记录的指针,但是由于之前获取到锁的线程已经将对象头中的MarkWord修改过了(并且现在还在执行同步体中的代码,即仍然持有着锁),所以此时对象头中的MarkWord与当前线程锁记录中MarkWord的值不同,导致CAS操作失败,然后该线程就会不停地循环使用CAS操作试图将对象头中的MarkWord替换为自己锁记录中MarkWord的值,(当循环次数或循环时间达到上限时停止循环)如果在循环结束之前CAS操作成功,那么该线程就可以成功获取到锁,如果循环结束之后依然获取不到锁,则锁获取失败,对象头中的MarkWord会被修改为指向重量级锁的指针,然后这个获取锁失败的线程就会被挂起,阻塞了。

当持有锁的那个线程执行完同步体之后,使用CAS操作将对象头中的MarkWord还原为最初的状态时(将对象头中指向锁记录的指针替换为Displaced Mark Word ),发现MarkWord已被修改为指向重量级锁的指针,因此CAS操作失败,该线程会释放锁并唤起阻塞等待的线程,开始新一轮夺锁之争,而此时,轻量级锁已经膨胀为重量级锁,所有竞争失败的线程都会阻塞,而不是自旋。

重量级锁:

java6之前的synchronized属于重量级锁,效率低下,因为monitor是依赖操作系统的Mutex Lock(互斥量)来实现的。

多线程竞争锁时,会引起线程的上下文切换(即在cpu分配的时间片还没有用完的情况下进行了上下文切换)。

操作系统实现线程的上下文切换需要从用户态转换到核心态,这个状态之间的转换需要相对较长的时间,时间成本相对较高。

在互斥状态下,没有得到锁的线程会被挂起阻塞,而挂起线程和恢复线程的操作都需要从用户态转入内核态中完成。

2.JVM通常可以对那些参数进行调优

https://blog.csdn.net/beyond59241/article/details/73719253

堆大小设置:年轻代大小,线程堆栈大小,年轻代中的比例,gc回收的年龄

回收器的选择:例如有吞吐量有限的回收器,也有追求最小gc停顿的回收器(CMS)

3.垃圾回收器的对比

https://blog.csdn.net/high2011/article/details/80177473

串行:垃圾回收时停掉所有用户线程,并单线程回收。为单线程环境而设计,最稳定以及效率高的收集器,可能会产生较长的停顿。

并行和parnew:Parallel收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。

CMS(concurrent marked sweep):以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。

G1:可预测停顿,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。

选择的依据应该来源:应用程序的场景,硬件的制约,吞吐量的需求

只是控制台的单线程程序,简单任务,并且机器配置不高(串行)

( 并行 )垃圾回收器是64bit server默认的垃圾回收器

(CMS)垃圾回收器是对并行垃圾回收器的一个优化,它以CPU和系统资源为代价,换取GC的延迟.

(G1垃圾回收器)是针对于大heap的垃圾回收器,如果heap分配的足够大,分的region的优先级回收策略会优先清理垃圾多的region.并且减少了内存空间碎片

4.组合索引的应用

https://www.cnblogs.com/wxgblogs/p/5743895.html

https://blog.csdn.net/u014590757/article/details/79590561

a.单独建立索引的效率小于组合索引,因为MYSQL只会选择一个索引进行。

b.MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

c.对于复合索引,在查询使用时,最好将条件顺序按找索引的顺序,这样效率最高;     select * from table1 where col1=A AND col2=B AND col3=D

d.检查是否用到了索引:可以通过执行时间来进行对比,也可以explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。

e.where b=45 and a=3 and c=5 .... 这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关。where里面的条件顺序在查询之前会被mysql自动优化。

5.tcp如果一个包的ack丢失

tcp接收端只确认数据流中第一个丢失字节为止的字节。(累计确认)如果在超时前后续的ack到达,则表明此时已经收到。如果超时,发送方会重传并重启定时器,然后接收方会丢弃该报文。

6.tcp的两个窗口

滑动窗口:在接收方,用于告诉发送方还有多少可用的缓存空间。由接收主机通过把Rwindow值放在给A的报文段中通知剩余空间。用于流量控制。

拥塞窗口:对于数据的发送端就是拥塞窗口了,拥塞窗口不代表缓存,拥塞窗口指某一源端数据流在一个RTT内可以最多发送的数据包数,cwnd:发送端窗口, 发送端主动控制控制cwnd

发送方窗口的上限值 = Min [ rwnd, cwnd ]

当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。

当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。

https://blog.csdn.net/lishanmin11/article/details/77092652

7.Java中的注解@及应用

https://www.cnblogs.com/cainiaodongdong/p/8018182.html

https://www.cnblogs.com/HDK2016/p/6914521.html

注解:像标签一样是对抽象事务的解释,注解就和其实同 class 和 interface 一样,注解也属于一种类型

@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target? –注解用于什么地方
@Inherited – 是否允许子类继承该注解
提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。 
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

8.Java中的反射能否更改具体的代码

https://blog.csdn.net/qq_27093465/article/details/52254924

可以做到修改属性的值,但是无法修改代码。

9.如何查看某个线程最近使用了哪些文件

https://blog.csdn.net/shinyv2062/article/details/51063074

ps -ef | grep xx

得到进程号之后 ls /proc/id号/fd | wc -l 可以得到

10.tcp的包是否都是等长度的

tcp可从缓存中取出并放入报文段的数据量受限与最大报文段长,通常根据最初确定的最大链路层帧长度来设置(MTU)

 

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值