面试问题总结

Object类自带哪些方法?

  • registerNatives() 私有方法
  • getClass() //返回此 Object 的运行类
  • hashCode() //用于获取对象的哈希值。
  • equals(Object obj) //用于确认两个对象是否“相同”。
  • clone() //创建并返回此对象的一个副本。
  • toString() //返回该对象的字符串表示。
  • notify() //唤醒在此对象监视器上等待的单个线程。
  • notifyAll() //唤醒在此对象监视器上等待的所有线程。
  • wait(long timeout) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或 者超过指定的时间量前,导致当前线程等待
  • wait(long timeout, int nanos) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
  • wait() //用于让当前线程失去操作权限,当前线程进入等待序列
  • finalize() //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法

String、StringBuffer、StringBulider的区别
Java语言有关字符串的类有三种:

  1. java.lang.String:一般作为简单字符串类型
  2. java.lang.StringBuffer:字符串缓冲区
  3. java.lang.StringBuilder:字符串缓冲区

对于三者的使用情况:

  1. 如果要操作少量数据用:String
  2. 单线程操作字符串缓冲区下操作大量数据用:StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据用:StringBuffer

区别在两个方面:

  • 三者在执行速度方面的区别:
StringBuilder>StringBuffer>String

String最慢的原因:

  1. String为字符串常量
  2. StringBuilder和StringBuffer是字符串变量
  3. String一旦被创建是不可修改,而StringBuilder和StringBuffer是变量可以被修改。

java中对String对象的操作实际上是一个不断创建新的对象并且将旧的对象收回的过程,所以执行的很慢。

  • 线程安全的区别
  1. StringBuilder是线程不安全的
    StringBuilder的方法没有synchronized关键字,所以不能保证线程安全;
  2. StringBuffer是线程安全的
    StringBuffer的很多方法都带有synchronized关键字,所以可以保证线程安全。

collection和collections的区别

  • Collections是个java.util下的,它包含有各种有关集合操作的静态方法。
  • Collection是个java.util下的接口,它是各种集合结构的父接口。

对String类了解多少

  • String对象创建的两种方式
    第一种:直接赋值
    String str1 = “ABCD”;
    创建的对象在方法区的常量池
    第二种:通过构造器创建xi
    String str2 = new String(“ABCD”)
    创建的对象在堆内存
  • String类是被final修饰的,不可继承、不可改变
  • String类的本事是字符数组char[]
  • String 对象的特点:不变性、常量池优化、final定义String
    不变性
    String对象的状态在被创建之后就不在发生变化
    作用:在一个对象被多个线程共享且被频繁访问时,可以省略同步和锁的时间,从而提高性能
    常量池优化
    当两个String对象拥有同一个值的时候,它们都只是引用了常量池中的同一个地址。
    final定义
    String类被final修饰,主要是为了“效率”和“安全性”,若String类允许被继承和改变,由于它的高度被使用率,可能会降低程序的性能,所以用final修饰。

**HashMap、Hashtable的区别?

  • 继承父类不同
    Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
  • 线程安全
  1. Hashtable线程安全(Hashtable中的方法是Synchronize的, HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。)
  2. HashMap线程不安全(HashMap中的方法在缺省情况下是非Synchronize的)

在多线程并发的环境下,可以直接使用Hashtable。

  • 是否提供contains方法
  1. HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey。
  2. Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
  • key和value是否允许null值
  1. Hashtable中,key和value都不允许出现null值。
  2. HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
  • hash值不同
  1. HashTable直接使用对象的hashCode。
  2. 而HashMap重新计算hash值。
  • 内部实现使用的数组初始化和扩容方式不同
  1. HashTable默认容量为11,扩容是将容量变为原来的2倍+1
  2. HashMap默认容量为16,扩容是将容量变为原来的2倍

HashMap的底层

  • hashMap 是一个用于存储key-value键值对的集合,每一个键值对也叫做Entry。这些Entry存储在一个数组当中,这个数组就是hashMap的主干。HashMap数组的每一个元素的初始值都是null。
  • hashMap的实现原理
    首先有一个每一个元素都是链表的数组,当添加一个元素(key-value)时,首先通过hash( )方法计算元素key的hash值,以此来确定插入数组中的位置,当计算出的位置相同时,由于存入数组中的元素是一个链表,则把这个key-value对插入链表头。当链表长度太长时,链表就会转换为红黑树,这样大大提高了查找的效率。

HashMap、LinkedHashMap、ConcurrentHashMap的异同?

  • HashMap:
    底层数据结构:哈希表、查询和插入速度快,线程不安全、效率高
  • LinkedHashMap:
    HashMap子类、底层数据结构:哈希表+链表、按插入顺序排序,遍历效率会高于HashMap。
  • HashTable:
    线程安全,但性能较差,已经不推荐使用
  • ConcurrentHashMap采用了分段锁技术保证线程安全
  1. 分段锁技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
  2. ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据
  3. 作用:专门用于解决并发问题(什么是并发?cpu在同一个时间执行多个任务,由多个线程实现)
  4. 线程安全效率低、无序、key和value都不允许null值。

Java中HashMap的key值要是为类对象,则该类需要满足什么条件?
需要重写类中的equals方法和hashCode方法


Comparable和Comparator接口是干什么的?列出它们的区别。

  • Comparable和Comparator都是接口,是用来比较和排序的
  • Comparable是自然排序,重写了compareTo()方法;
  • Comparator是比较器排序,重写了compare()方法;
  • 常见的排序
  1. 数组:Arrays.sort();
  2. List:conlections.sort();
  3. Set:TreeSet();
  4. Map:TreeMap();

什么是流?按照传输的单位,分成哪两种流?他们的父类叫什么?

  • 流的定义:
    Java程序中对数据的输入和输出称为流。
    形象的说:流就是一个管道里面有流水,这个管道连接了文件和程序。
  • 流的分类:字节流,字符流
  1. 字节流的父类:InputStream OutputStream
  2. 字符流的父类:Reader Writer

什么叫对象序列化,什么是反序列化,如何实现对象序列化?

  • 对象的序列化: 把对象转换为字节序列的过程;
    序列化实质:将对象转换成二进制
  • 对象的反序列化: 把字节序列恢复为对象的过程;
  • 实现序列化:只需实现Serializable接口就行;
    先要创建某些utputStream对象,然后将其封装在一个ObjectOutputStream对象内,再调用writeObject方法,即可序列化一个对象。

开启线程的三种方式

  1. 继承Thread类,并重写run方法,创建该类对象,调用start方法开启线程。

  2. 实现Runnable接口,重写run方法,创建Thread类对象,将Runnable子类对象传递给Thread类对象。调用start方法开启线程。

  3. 实现Callable接口,重写call(相当于run)方法,创建Callable子类对象,创建FutureTask对象,将Callable子类对象传递给FutureTask对象,创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可获得线程执行完之后的返回值。


进程,线程,协程之间的区别
1、进程
进程是系统进行资源分配和调度的基本单位。由于进程间是隔离的且每个进程都有自己的独立内存空间,因此线程相对安全。一个进程内至少有一个线程
2、线程
线程属于进程,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。共享进程所拥有的全部资源。但相比进程不够稳定容易丢失数据。
3、协程
协程属于线程,是一种用户态的轻量级线程,协程的调度完全由用户控制。一个线程可以有多个协程。进程和线程是同步机制,而协程是异步。协程能保留上次调用时的状态,每次重入时,就相当于进入上一次调用的状态。


wait()和sleep()方法的区别
添加链接描述


2.1、等待(wait)/通知机制(notify)
方法 wait() 说明

方法wait()的作用是使当前执行的线程进行等待,wait() 方法是Object 类的方法,该方法用来将当前线程处于预执行状态,并且在调用wait()方法的代码处停止执行,直到接到通知或被终止。

在调用wait()之前,线程必须获得对象的锁,即只能在同步方法或代码块中调用wait()方法。在执行wait()方法后,当前线程释放锁。

如果 调用 wait() 时 没有持有适当的锁,则抛出IllegalMonitorStateException, 它是 RuntimeException 的 一个 子类。

方法 notify()说明

notify()方法在调用前,线程也必须获得该对象的锁,否则会抛出 IllegalMonitorStateException。该方法用来通知哪些等待该对象锁的其它线程,如果有多个线程等待,则有线程规划器随机挑选一个 wait 中的线程,对其发出notify 通知并使它准备回去该对象的锁。

在执行notify() 方法后,当前线程不会马上释放对象锁,要等待执行notify() 方法的线程执行完毕后,当前线程才会真正释放锁。

等待/通知机制总结:

wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出, 进入 等待队列,直到被再次唤醒。

notify() 方法可以随机唤醒一个等待队列中的线程,并使该线程退出等待队列, 进入 可运行状态,也就是 notify()方法仅通知“ 一个” 线程。

notifyAll()方法可以使所有正在等待队列中的全部线程 从等待状态退出, 进入可运行状态。 此时,优先级最高的那个线程最先执行,但也有可能随机执行, 因为 这要取决于JVM虚拟机的实现。

2.2、使用方法join
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量耗时运算,主线程往往早于子线程结束之前结束。这时如果主线程想等待子线程完成之后再结束,比如子线程处理一个数据,主线程要取得子线程的结果进行计算,就要用到join()方法了。

1、join()方法与 synchronized 的区别是:

join()方法内部使用 wait()方法进行等待,而synchrinized 关键字使用的是对象监视器原理实现同步。

2、join()方法与sleep()方法的区别:

join()方法内部是使用wait()方法来实现,所以join()方法具有释放锁的特点,而sleep()方法只是释放了CPU资源,但是不释放锁。

2.3、使用ThreadLocal
变量值得共享可以使用 publict static 变量的形式,所有的线程都使用同一个变量。如果每一个线程都有自己的共享变量该如何解决呢?JDK中提供了ThreadLocal 正式为了解决这样的问题。类 ThreadLocal 主要解决的是每个线程都可以拥有自己线程内的共享变量。

ThreadLocal 主要解决的是线程之间的隔离性,每个线程都有自己独有的值,互不干扰,同时该特性在父子关系的线程中也有效。

2.4、使用InheritableThreadLocal
使用类InheritableThreadLocal 可以在子线程中取得父线程继承下来的值。

但在使用类InheritableThreadLocal 时需要注意一点,如果子线程在取得值得同时,主线程将其中的值进行了修改,那么子线程取到的值还是旧值。


在Java中wait和seelp方法的不同

  1. sleep()方法属于Thread类; wait()方法属于Object类
  2. sleep()方法不会释放锁;wait()方法会释放锁
  3. sleep()方法需要捕获异常;wait()不需要
  4. sleep()方法时间到了就会自然醒;wait()需要notify或notifyAll()唤醒。

谈谈ThreadLocal关键字1
参考文章
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。


线程问题
run方法和start方法和join方法
线程的生命周期


collection和collections的区别

Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。
Collection是个java.util下的接口,它是各种集合结构的父接口。
List, Set, Map是否继承自Collection接口? List,Set是 Map不是


死锁

  1. 什么死锁?ruogan
    两个或多个线程在执行过程中,因争夺资源而产生的一种相互等待的状态。
  2. 产生死锁的四个必要条件
    互斥条件:一个资源每一次只能被一个进程占用
    请求和保持条件:一个进程因请求已经被其他进程所占用的资源而发生阻塞时,对自己以获得的资源保持不放
    不剥夺条件:任何资源在未被该进程释放前,其他进程都无法对他进行剥夺占用
    循环等待条件:若干个进程形成一个头尾相连,相互等待的结果。
  3. 如何避免死锁参考这篇文章
    加锁顺序:
    当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。但是,这种方式需要你事先知道所有可能会用到的锁(译者注:并对这些锁做适当的排序),但总有些时候是无法预知的。
    加锁时限:
    死锁检测

Lock和synchronized的选择

总结来说,Lock和synchronized有以下几点不同:

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。


锁的相关概念介绍

  1. 可重入锁
    如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。
  2. 可中断锁
      可中断锁:顾名思义,就是可以相应中断的锁。
      在Java中,synchronized就不是可中断锁,而Lock是可中断锁。
      如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
  3. 公平锁
    公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。 
    非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。
    在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。
    而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
     4.读写锁
    读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。
    ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
    可以通过readLock()获取读锁,通过writeLock()获取写锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值