java 面试题汇总

java

一、Java 基础

1.JDK 和 JRE 有什么区别?

JDK是java开发工具包,提供java的开发环境和运行环境。包括编译器、开发工具和更多的类库等。JDK包含了JRE。

JRE是java运行环境,为java的运行提供了所需环境。包括JVM虚拟机和基本的类库。

2.== 和 equals 的区别是什么?

  • ==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同
  • ==是指对内存地址进行比较 , equals()是对字符串的内容进行比较
  • ==指引用是否相同, equals()指的是值是否相同

3.两个对象的 hashCode相同,则 equals也一定为 true,对吗?

不一定相同。正常情况下,因为equals()方法比较的就是对象在内存中的值,如果值相同,那么Hashcode值也应该相同。但是如果不重写hashcode方法,就会出现不相等的情况。

4.final 在 java 中有什么作用?

用于修饰类、类属性和类方法。

特征:凡是引用final关键字的地方皆不可修改!

  • (1)修饰类:表示该类不能被继承;
  • (2)修饰方法:表示方法不能被重写;
  • (3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

5.java 中的 Math.round(-1.5) 等于多少?

Math.round(-1.5)的返回值是-1

6.String 属于基础的数据类型吗?

String不是基本的数据类型,是final修饰的java类。

java中的基本类型一共有8个,它们分别为:

  • 1 字符类型:byte,char
  • 2 基本整型:short,int,long
  • 3 浮点型:float,double
  • 4 布尔类型:boolean

7.java 中操作字符串都有哪些类?它们之间有什么区别?

String、StringBuffer、StringBuilder

  • String : final修饰,String类的方法都是返回new String。即对String对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。
  • StringBuffer : 对字符串的操作的方法都加了synchronized,保证线程安全。
  • StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。

8.String str="i"与 String str=new String("i")一样吗?

String str = "i"; 这个只是一个引用,内存中如果有“i"的话,str就指向它;如果没有,才创建它;

如果你以后还用到"i"这个字符串的话并且是这样用:

String str1 = "i"; String str2 = "i"; String str2 = "i"; 这4个变量都共享一个字符串"i"。

而String str = new String("i");是根据"i"这个String对象再次构造一个String对象,将新构造出来的String对象的引用赋给str。

9.如何将字符串反转?

  • 1. 使用Array.Reverse方法
  • 2.使用字符缓存
  • 3.使用StringBuilder
  • 4.栈是一个很神奇的数据结构。我们可以使用它后进先出的特性来对数组进行反转。先将数组所有元素压入栈,然后再取出,顺序很自然地就与原先相反了。
  • 5.使用逻辑异或也可以进行反转
  • 6.使用指针
  • 7.使用递归
  • 8.使用委托
  • 9. System.Enumerable里提供了默认的Reverse扩展方法,我们可以基于该方法来对String类型进行扩展

10.String 类的常用方法都有那些?

  • 1.求字符串长度
  • 2.求字符串某一位置字符
  • 3.提取子串
  • 4.字符串比较
  • 5.字符串连接
  • 6.字符串单个字符查找
  • 7.字符串中字符的大小写转换
  • 8.字符串中字符的替换
  • 9.其他类方法

 

11.抽象类必须要有抽象方法吗?

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。

如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

12.普通类和抽象类有哪些区别?

  • 抽象类不能被实例化
  • 抽象类可以有抽象方法,抽象方法只需申明,无需实现
  • 含有抽象方法的类必须申明为抽象类
  • 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
  • 抽象方法不能被声明为静态
  • 抽象方法不能用private修饰
  • 抽象方法不能用final修饰

13.抽象类能使用 final 修饰吗?

不能,抽象类是被用于继承的,final修饰代表不可修改、不可继承的。

14.接口和抽象类有什么区别?

他们都不能实例化对象,都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。

  • 1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
  • 2、抽象类要被子类继承,接口要被类实现。
  • 3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
  • 4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  • 5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
  • 6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
  • 7、抽象类里可以没有抽象方法
  • 8、如果一个类里有抽象方法,那么这个类只能是抽象类
  • 9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
  • 10、接口可继承接口,并可多继承接口,但类只能单根继承。

15.java 中 IO 流分为几种?

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

16.BIO、NIO、AIO 有什么区别?

BIO:线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。BIO是一个连接一个线程。

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。


NIO:线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。NIO是一个请求一个线程。

同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。


AIO:线程发起IO请求,立即返回;内存做好IO操作的准备之后,做IO操作,直到操作完成或者失败,通过调用注册的回调函数通知线程做IO操作完成或者失败。AIO是一个有效请求一个线程。异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

17.Files的常用方法都有哪些?

  • Files.exists() 检测文件路径是否存在
  • Files.createFile()创建文件
  • Files.c\'r\'eateDirectory()创建文件夹
  • Files.delete() 删除文件或者目录
  • Files.copy() 复制文件
  • Files.move() 移动文件
  • Files.size()查看文件个数
  • Files.read() 读取文件
  • Files.write()写入文件

二、容器

18.java 容器都有哪些?

List,Map,Set ,Collection ,List ,LinkedList ,ArrayList ,Vector ,Stack ,Set

Map ,Hashtable ,HashMap ,WeakHashMap

19.Collection 和 Collections 有什么区别?

Collection是集合类的上级接口,继承与他的接口主要有Set 和List.

Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

20.List、Set、Map 之间的区别是什么?

  • List中的元素,有序、可重复、可为空;
  • Set中的元素,无序、不重复、只有一个空元素;
  • Map中的元素,无序、键不重,值可重、可一个空键、多可空值;

21.HashMap 和 Hashtable 有什么区别?

HashMap时HashTable的轻量级实现(非线程安全的实现),它们都实现了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上高于HashTable。

HashMap允许将null作为一个entry的key或者value,而HashTable不允许。

HashMap去掉了HashTable的contains方法,改成containsValue和containsKey方法。

二者最大的不同是,HashTable的方法是synchronized(线程安全的),而HashMap不是,在多个线程访问HashTable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供外同步。

22.如何决定使用 HashMap 还是 TreeMap?

 

23.说一下 HashMap 的实现原理?

HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用LinkedList来解决碰撞问题,当发生碰撞了,对象将会储存在LinkedList的下一个节点中。 HashMap在每个LinkedList节点中储存键值对对象。

24.说一下 HashSet 的实现原理?

HashSet实际上是一个HashMap实例,都是一个存放链表的数组。它不保证存储元素的迭代顺序;此类允许使用null元素。HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象private static final Object PRESENT = new Object();

HashSet中add方法调用的是底层HashMap中的put()方法,而如果是在HashMap中调用put,首先会判断key是否存在,如果key存在则修改value值,如果key不存在这插入这个key-value。而在set中,因为value值没有用,也就不存在修改value值的说法,因此往HashSet中添加元素,首先判断元素(也就是key)是否存在,如果不存在这插入,如果存在着不插入,这样HashSet中就不存在重复值。

所以判断key是否存在就要重写元素的类的equals()和hashCode()方法,当向Set中添加对象时,首先调用此对象所在类的hashCode()方法,计算次对象的哈希值,此哈希值决定了此对象在Set中存放的位置;若此位置没有被存储对象则直接存储,若已有对象则通过对象所在类的equals()比较两个对象是否相同,相同则不能被添加。

25.ArrayList 和 LinkedList 的区别是什么?

  • 1. ArrayList的实现是基于数组,LinkedList的实现是基于双向链表。
  • 2. 对于随机访问,ArrayList优于LinkedList
  • 3. 对于插入和删除操作,LinkedList优于ArrayList
  • 4. LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

26.如何实现数组和 List 之间的转换?

List转数组:toArray(arraylist.size()方法

数组转List:Arrays的asList(a)方法

List<String> arrayList = new ArrayList<String>();

arrayList.add("s");

arrayList.add("e");

arrayList.add("n");

/**

* ArrayList转数组

*/

int size=arrayList.size();

String[] a = arrayList.toArray(new String[size]);

//输出第二个元素

System.out.println(a[1]);//结果:e

//输出整个数组

System.out.println(Arrays.toString(a));//结果:[s, e, n]

/**

* 数组转list

*/

List<String> list=Arrays.asList(a);

/**

* list转Arraylist

*/

List<String> arrayList2 = new ArrayList<String>();

arrayList2.addAll(list);

System.out.println(list);

27.ArrayList 和 Vector 的区别是什么?

(1)同步性:Vector是线程安全的,用synchronized实现线程安全,而ArrayList是线程不安全的,如果只有一个线程会访问到集合,那最好使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们再去考虑和编写线程安全的代码。

(2)数据容量增长:二者都有一个初始容量大小,采用线性连续存储空间,当存储的元素的个数超过了容量时,就需要增加二者的存储空间,Vector增长原来的一倍,ArrayList增加原来的0.5倍。

28.Array 和 ArrayList 有何区别?

  • 1. Array类型的变量在声明的同时必须进行实例化(至少得初始化数组的大小),而ArrayList可以只是先声明。
  • 2. Array只能存储同构的对象,而ArrayList可以存储异构的对象。
  • 同构的对象是指类型相同的对象,若声明为int[]的数组就只能存放整形数据,string[]只能存放字符型数据,但声明为object[]的数组除外。
  • 而ArrayList可以存放任何不同类型的数据(因为它里面存放的都是被装箱了的Object型对象,实际上ArrayList内部就是使用"object[] _items;"这样一个私有字段来封装对象的)
  • 3. 在CLR托管对中的存放方式
  • Array是始终是连续存放的,而ArrayList的存放不一定连续。
  • 4. 初始化大小
  • Array对象的初始化必须只定指定大小,且创建后的数组大小是固定的,
  • 而ArrayList的大小可以动态指定,其大小可以在初始化时指定,也可以不指定,也就是说该对象的空间可以任意增加。
  • 5. Array不能够随意添加和删除其中的项,而ArrayList可以在任意位置插入和删除项。

29.在 Queue 中 poll和 remove有什么区别?

poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。

30.哪些集合类是线程安全的?

vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。

statck:堆栈类,先进后出

hashtable:就比hashmap多了个线程安全

enumeration:枚举,相当于迭代器

 

31.迭代器 Iterator 是什么?

  • 迭代器是一个“可遍历STL容器内全部或者部分元素“的对象
  • 一个迭代器指出容器中的一个特定位置
  • 具有遍历复杂数据结构的能力

32.Iterator 怎么使用?有什么特点?

boolean hasNext() ;判断迭代器中是否还有下一个元素,有则返回true

Object next(); 返回迭代器中下一个元素

void remove() ; 删除集合里上一个next方法调用的时候返回的对象元素

void forEachRemaining(Consumer action) ;使用Lambdda表达式的形式输出Iterator中所以的元素。注意该方法其实是间接调用next()方法进行遍历,所以再次是next()方法的时候Iterator中的对象已经被遍历完了。

  1. Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。
  2. Iterator遍历集合元素的过程中可以通过remove方法来移除集合中的元素。
  3. Iterator必须依附某个Collection对象而存在,Iterator本身不具有装载数据对象的功能。
  4. Iterator.remove方法删除的是上一次Iterator.next()方法返回的对象。
  5. 强调以下next()方法,该方法通过游标指向的形式返回Iterator下一个元素。

33.Iterator 和 ListIterator 有什么区别?

  1. 1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能
  2. 2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
  3. 3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
  4. 4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

34.怎么确保一个集合不能被修改?

我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。

同理:Collections包也提供了对list和set集合的方法。

Collections.unmodifiableList(List)

Collections.unmodifiableSet(Set)

 

三、多线程

35.并行和并发有什么区别?

并发是同一时间应对(dealing with)多件事情的能力;

并行是同一时间动手做(doing)多件事情的能力;

36.线程和进程的区别?

  • 进程是资源分配的最小单位,线程是程序执行的最小单位。
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
  • 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

37.守护线程是什么?

守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java里线程分2种,

1、守护线程,比如垃圾回收线程,就是最典型的守护线程。

2、用户线程,就是应用程序里的自定义线程。

38.创建线程有哪几种方式?

  1. 1. 继承 Thread 类,然后调用 start 方法。
  2. 2. 实现 Runnable 接口的 run 方法, 然后再用 Thread 类包裹后,调用 start 方法。
  3. 3. 实现 Callable 接口的 call 方法,用 FutureTask 类包裹 Callable 对象。然后再用 Thread 类包裹 FutureTask 类,并调用 start 方法。call() 方法可以有返回值。

39.说一下runnable 和 callable 有什么区别?

Runnable没有返回值;Callable可以返回执行结果,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果

Callable接口的call()方法允许抛出异常;Runnable的run()方法异常只能在内部消化,不能往上继续抛

40.线程有哪些状态?

线程状态有 5 种,新建,就绪,运行,阻塞,死亡。


 

41.sleep 和 wait 有什么区别?

  • 1,sleep方法是Thread类的静态方法,wait()是Object超类的成员方法
  • 2,sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
  • 而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
  • 3,sleep方法需要抛异常,wait方法不需要
  • 4,sleep方法可以在任何地方使用,
  • wait方法只能在同步方法和同步代码块中使用

42.notify和 notifyAll有什么区别?

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

43.线程的 run和 start有什么区别?

调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。

一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

44.创建线程池有哪几种方式?

newSingleThreadExecutor

newFixedThreadPool

newCachedThreadPool

newScheduledThreadPool

newSingleThreadScheduledExecutor

newWorkStealingPool

45.线程池都有哪些状态?

线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。

46.线程池中 submit和 execute方法有什么区别?

  • 1、接收的参数不一样
  • 2、submit有返回值,而execute没有
  • 用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。
  • 然后我就可以把所有失败的原因综合起来发给调用者。
  • 而最大的用处应该是第二点。
  • 3、submit方便Exception处理
  • 意思就是如果你在你的task里会抛出checked或者unchecked exception,
  • 而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

47.在 java 程序中怎么保证多线程的运行安全?

  • 1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作
  • 2.可见性:一个线程对主内存的修改可以及时地被其他线程看到
  • 3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序

48.多线程锁的升级原理是什么?

synchronized 关键字编译后会在同步块的前后添加上 montorenter 和 monitorexit 两个字节码指令,这两个字节码指令都需要一个指向锁定和解锁对象的 reference,如果指定了同步的对象reference就指向这个对象,如果修饰的是方法,如果是类方法就指向Class对象,如果是实例方法就指向这个实例。

在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

49.什么是死锁?

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。


 

50.怎么防止死锁?

死锁预防

我们可以通过破坏死锁产生的4个必要条件来 预防死锁,由于资源互斥是资源使用的固有特性是无法改变的。

  1. 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
  2. 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
  3. 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。

51.ThreadLocal 是什么?有哪些使用场景?

ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

在并发编程中时常有这样一种需求:每条线程都需要存取一个同名变量,但每条线程中该变量的值均不相同。

52.说一下 synchronized 底层实现原理?

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。

Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

  1. 普通同步方法,锁是当前实例对象
  2. 静态同步方法,锁是当前类的class对象
  3. 同步方法块,锁是括号里面的对象

53.synchronized 和 volatile 的区别是什么?

  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

54.synchronized 和 Lock 有什么区别?

1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;

2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

55.synchronized 和 ReentrantLock 区别是什么?

  • 除了synchronized的功能,多了三个高级功能. 等待可中断,公平锁,绑定多个Condition.
  • 1.等待可中断 在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待. tryLock(long timeout, TimeUnit unit)
  • 2.公平锁 按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁. new RenentrantLock(boolean fair)
  • 3.绑定多个Condition 通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.通过await(),signal(); 总的来说,lock更加灵活。

56.说一下 atomic 的原理?

1、直接操作内存,使用Unsafe 这个类

2、使用 getIntVolatile(var1, var2) 获取线程间共享的变量

3、采用CAS的尝试机制(核心所在)

4、使用Atomic ,是在硬件上、寄存器尽心阻塞,而不是在线程、代码上阻塞。

5、这个是通俗说法ABA的问题

以上是 java 面试题汇总 的全部内容, 来源链接: utcz.com/z/389830.html

回到顶部