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 问题
  • 乐观锁的业务场景及实现方式
原文地址:https://www.cnblogs.com/xuhewei/p/9684579.html