面试被怼集(字节跳动篇)

1.面试官:你说说IO和NIO的区别吧:

我:主要的区别就是IO在读取资源的时候如果读不到就会阻塞在那里,但是NIO能不能读到都会立刻返回一个结果,线程可以去做其他事。

面试官:你说tomcat是IO还是NIO模式的

我:tomcat是NIO的啊(这里答错了,其实IO和NIO tomcat都可以支持,是可以预先配置的,其实大多数都是IO和NIO都可以支持的,包括Netty在内)

面试官:那tomcat去请求一个资源的时候可能需要花费一段时间,几分钟的过程,那你说它还是NIO的吗?

我:。。。。。。。。。。。。。。。。(我觉得哪里不对,像是哪里被混淆了,但是又无法反驳)

 

面试结束思考:我觉得NIO和IO所要解释的场景不是整个请求过程,而是请求结果,无论是要需要一小时还是两小时,这个是请求的中间过程,和IO或者NIO模式本身没有任何关系,IO和NIO是请求之后的完成动作(读没读到资源都算是请求完成),而不是面向请求过程的。

暂时是这样想的,各位路过大神感兴趣的来指点一下谢谢。

 

2.面试官:你是怎么遍历Map的

我:for(Map.Entry<String,String>entry:a.entrySet()){}

面试官:你还知道其他遍历方式吗?

我:也可以用迭代器吧。

面试官:那你为什么用这种方式?

我:因为之前有看到网上说这样遍历大量数据的时候比较快

面试官:为什么比较快?

我:。。。。。。。。。

马后炮:其实面试官这里想问的应该是entrySet和keySet的区别。

keySet():将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 

  Set<String> keySet = map.keySet();

   Iterator<String> it = keySet.iterator();//有了Set集合,就可以获取其迭代器。
                
        while(it.hasNext()){
                String key = it.next();
                String value = map.get(key);//有了键可以通过map集合的get方法获取其对应的值。
                        
                System.out.println("key: "+key+"-->value: "+value);//获得key和value值
                }

entrySet():Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。 Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 。

keySet():迭代后只能通过get()取key 
entrySet():迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 

keySet是键的集合,Set里面的类型即key的类型
entrySet是 键-值 对的集合,Set里面的类型是Map.Entry

3.面试官:讲一下你在线程池里都配置哪些参数:

 

参数名说明
corePoolSize 线程池维护线程的最少数量
maximumPoolSize 线程池维护线程的最大数量
keepAliveTime 线程池维护线程所允许的空闲时间
workQueue 任务队列,用来存放我们所定义的任务处理线程
threadFactory 线程创建工厂
handler 线程池对拒绝任务的处理策略

 

面试官:既然你提到拒绝策略,那讲一讲你都知道哪些拒绝策略吧

1)CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

  1.  
    publicvoid rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    if (!e.isShutdown()) {
  3.  
    r.run();
  4.  
    }
  5.  
    }

这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。(开始我总不想丢弃任务的执行,但是对某些应用场景来讲,很有可能造成当前线程也被阻塞。如果所有线程都是不能执行的,很可能导致程序没法继续跑了。需要视业务情景而定吧。)

2)AbortPolicy:处理程序遭到拒绝将抛出运行时 RejectedExecutionException

  1.  
    publicvoid rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    throw new RejectedExecutionException();
  3.  
    }

这种策略直接抛出异常,丢弃任务。(jdk默认策略,队列满并线程满时直接拒绝添加新任务,并抛出异常,所以说有时候放弃也是一种勇气,为了保证后续任务的正常进行,丢弃一些也是可以接收的,记得做好记录)

3)DiscardPolicy:不能执行的任务将被删除

publicvoid rejectedExecution(Runnable r, ThreadPoolExecutor e) {}

这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。

4)DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

  1.  
    publicvoid rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  2.  
    if (!e.isShutdown()) {
  3.  
    e.getQueue().poll();
  4.  
    e.execute(r);
  5.  
    }
  6.  
    }

 

该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。

4.MyBatis中#与$的区别:

1.两者都是动态的向sql语句中传入需要的参数

2.#传入的参数在SQL中显示为字符串

     eg:select id,name,age from student where id =#{id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id ='1'.

3.$传入的参数在SqL中直接显示为传入的值

    eg:select id,name,age from student where id =${id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1.

4.#可以防止SQL注入的风险(语句的拼接)

5.但是如果使用在order by 中就需要使用 $.

我觉得#与的区别最大在于:#{} 传入值时,sql解析时,参数是带引号的,而{}传入值,sql解析时,参数是不带引号的。

一 : 理解mybatis中 $与#

    在mybatis中的$与#都是在sql中动态的传入参数。

    eg:select id,name,age from student where name=#{name}  这个name是动态的,可变的。当你传入什么样的值,就会根据你传入的值执行sql语句。

二:使用$与#

   #{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符 。

   ${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。

    传入一个不改变的字符串或者传入数据库字段(列名),例如要传入order by 后边的参数

     这种情况下必须使用${}。

综上,#{}方式一般用于传入字段值,并将该值作为字符串加到执行sql中,一定程度防止sql注入;

           ${}方式一般用于传入数据库对象,例如传入表名,不能防止sql注入,存在风险。

           模糊查询中,如果使用如下方式:select * from reason_detail where reason_en like '%${reason}%',此处只能使用$,使用#的话,反而会被解析为列,报错java.sql.SQLException: Column 'reason' not found

 

 

5.面试官问了一个线程的名字怎么命名还是自定义线程的来着记不太清了:

线程名字的定义:

public final void setName(String name)
public final String getName()

 

当使用Runnable创建线程时,Runnable中并没有getName和setName,那么想要获取运行当前代码的线程名字就需要调用 Thread类中的public static Thread currentThread()获取当前线程,在调用getName()获取线程名

publicclassThreadName{publicstaticvoidmain(Stringargs[]){Threadt=newThread(newMyThreadName(),"线程A");t.start();t.run();}}classMyThreadNameimplementsRunnable{@Overridepublicvoidrun(){System.err.println("threa-name="+Thread.currentThread().getName());}}

 

自定义线程:

/*
自定义线程的创建方式:

方式一 : 
    1. 自定义一个类继承Thread类。
    2. 重写Thread类的run方法,把自定义线程的任务代码写在run方法上。
    3. 创建Thread的子类对象,并且调用start方法启动一个线程。 
        
    注意:千万不要直接调用run方法,调用start方法的时候线程就会开启,线程一旦开启就会执行run方法中代码,如果直接调用
    run方法,那么就 相当于调用了一个普通的方法而已。

方式二:
    1. 自定义一个类实现Runnable接口。
    2. 实现Runnable接口 的run方法,把自定义线程的任务定义在run方法上。
    3. 创建Runnable实现类对象。
    4. 创建Thread类 的对象,并且把Runnable实现类的对象作为实参传递。
    5. 调用Thread对象 的start方法开启一个线程。


问题1: 请问Runnable实现类的对象是线程对象吗?
    Runnable实现类的对象并 不是一个线程对象,只不过是实现了Runnable接口 的对象而已。
    只有是Thread或者是Thread的子类才是线程 对象。

问题2: 为什么要把Runnable实现类的对象作为实参传递给Thread对象呢?作用是什么?
    作用就是把Runnable实现类的对象的run方法作为了线程的任务代码去执行了。

推荐使用: 第二种。 实现Runable接口的。 
原因: 因为java单继承 ,多实现的。



 */

public class Demo3 implements Runnable{

    @Override
    public void run() {
        /*System.out.println("this:"+ this);
        System.out.println("当前线程:"+ Thread.currentThread());*/
        for(int i = 0 ; i < 100 ; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    
    public static void main(String[] args) {
        //创建Runnable实现类的对象
        Demo3 d = new Demo3();
        //创建Thread类的对象, 把Runnable实现类对象作为实参传递。
        Thread thread = new Thread(d,"狗娃");  //Thread类使用Target变量记录了d对象,
        //调用thread对象的start方法开启线程。
        thread.start();
        
        
        for(int i = 0 ; i < 100 ; i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
        
    } 
    
    /*
      Thread类 的run方法
      
     *  @Override
        public void run() {
            if (target != null) {
                target.run();  //就相当于Runnable实现类的对象的run方法作为了Thread对象的任务代码了。
            }
        }
    */
}

public classThreadName{publicstaticvoidmain(Stringargs[]){Threadt=newThread(newMyThreadName(),"线程A");t.start();t.run();}}classMyThreadNameimplementsRunnable{@Overridepublicvoidrun(){System.err.println("threa-name="+Thread.currentThread().getName());}}

posted @ 2019-04-29 12:39  北回归线的喵  阅读(788)  评论(0编辑  收藏  举报