zoukankan      html  css  js  c++  java
  • java(集合、线程)面试要点2

    集合

    • List 和 Set 区别  

      collection的子接口list和se  

      list:①,允许重复对象②,可以插入多个null元素③,有序容器

        ④,常用的实现类有ArrayList、LinkedList和Vector,ArrayList使用索引随意访问,而LinkedList则更适合经常从list中添加删除的场合。

      set:①,不能有重复元素②,无序容器③,只能有一个null元素

        ④,实现类有HashSet、LinkedHashSet、TreeSet

      map:①,map不是collection的子接口或实现类,是一个接口②,map的每个Entry都有两个对象,一个键一个值,键不能重复。

        ③,可以有多个null值,但只能有一个null键

        ④,实现类有HashMap、LinkedHashMap、HashTable、TreeMap

    •   List 和 Map 区别
    • Arraylist 与 LinkedList 区别
    • ArrayList 与 Vector 区别
    • HashMap 和 Hashtable 的区别

      1、作者不同

      2、时间不同,Hashtable自java发布以来,HashMap产生于jdk1.2

      3、继承的父类不同HashTable继承Dictinary类,HashMap继承AbstractMap类

      4、Hashtable既不支持Null key也不支持Null value  

      5、HashMap中,null可以作为键,判断是否存在一个键不能使用get()方法,当职位null时get()方法返回null,应该使用containsKey()方法判断。

      6、Hashtable线程安全,每个方法都加入了Synchronize方法。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

      7、为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。hash值的方法不同,Hashtable直接使用对象的hashCode,hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。

    Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。 
    HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

    HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算

    为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。

    • HashSet 和 HashMap 区别
    • HashMap 和 ConcurrentHashMap 的区别

       

    • HashMap 的工作原理及代码实现

       https://www.cnblogs.com/chengxiao/p/6059914.html

    • ConcurrentHashMap 的工作原理及代码实现

       https://blog.csdn.net/helei810304/article/details/79786606

    线程

    • 创建线程的方式及实现

      https://blog.csdn.net/m0_37840000/article/details/79756932  

    创建线程的四种方式:

        ①继承Thread类,重写run方法

        ② 实现Runnable接口,重写run方法

        ③使用线程池例如用Executor框架

        ④使用Callable和Future创建线程

    --------继承Thread方式--------------------------------------------------------------

    public class MyThread extends Thread{
    
      public void  run(){//重写run方法}
    
    }
    
    public class Test{
    
      public static void main(String[] args){
    
        new MyThread().start();//启动线程
    
       }
    
    }

      -----实现Runnable接口方式-----------------------------------------------

    public class MyThread implements Runnable{
      public void run(){}  
    }
    public class test{
       public static void main(String[] args){
              MyThrea tt = new MyThread();
              Thread thread = new Thread(tt);
               thread.start();
        } 
    }

      ------------------------使用Callable和Future创建线程---------------------

    1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
    
    2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
    
    3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
    
    4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
    代码实例:
    
    public class Main {
    
      public static void main(String[] args){
    
       MyThread3 th=new MyThread3();
    
       //使用Lambda表达式创建Callable对象
    
         //使用FutureTask类来包装Callable对象
    
       FutureTask<Integer> future=new FutureTask<Integer>(
    
        (Callable<Integer>)()->{
    
          return 5;
    
        }
    
        );
    
       new Thread(task,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
    
        try{
    
        System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
    
        }catch(Exception e){
    
        ex.printStackTrace();
    
       }
    
      }
    
    }
    • sleep() 、join()、yield()有什么区别
    1、sleep()
    使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
    例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
    总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
    2、join()
    join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
    3、yield()
    该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
    4、wait:Object类的方法,必须放在循环体和同步代码块中,执行该方法的线程会释放锁,进入线程等待池中等待被再次唤醒(notify随机唤醒,notifyAll全部唤醒,线程结束自动唤醒)即放入锁池中竞争同步锁
    • 说说 CountDownLatch 原理
    • 说说 CyclicBarrier 原理
    • 说说 Semaphore 原理
    • 说说 Exchanger 原理
    • 说说 CountDownLatch 与 CyclicBarrier 区别
    • ThreadLocal 原理分析
    • 讲讲线程池的实现原理
    • 线程池的几种方式与使用场景
    • 线程的生命周期

      

    (1)生命周期的五种状态
    
    新建(new Thread)
    当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
    例如:Thread  t1=new Thread();
    
    就绪(runnable)
    线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
    
    运行(running)
    线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
    
    死亡(dead)
    当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
    
    自然终止:正常运行run()方法后终止
    
    异常终止:调用stop()方法让一个线程终止运行
    
    堵塞(blocked)
    由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
    
    正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
    
    正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
    
    被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)

    锁机制

    • 说说线程安全问题
    • volatile 实现原理
    • synchronize 实现原理
    • synchronized 与 lock 的区别
    • CAS 乐观锁
    • ABA 问题
    • 乐观锁的业务场景及实现方式
  • 相关阅读:
    5.单表查询
    3.数据类型1
    3.数据类型2
    mysql用户操作和权限管理
    【剑指Offer】面试题27. 二叉树的镜像
    【LeetCode】160. 相交链表
    【剑指Offer】面试题52. 两个链表的第一个公共节点
    【LeetCode】206. 反转链表
    【剑指Offer】面试题24. 反转链表
    LeetCode题解分类汇总(包括剑指Offer和程序员面试金典,持续更新)
  • 原文地址:https://www.cnblogs.com/xuhewei/p/9684579.html
Copyright © 2011-2022 走看看