前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JAVA宝典-面试题-多线程篇(含答案)

JAVA宝典-面试题-多线程篇(含答案)

作者头像
Java宝典
发布2021-07-15 15:23:08
3840
发布2021-07-15 15:23:08
举报

下面是Java线程相关的高频面试题(含答案),你可以用它来好好准备面试。

  • 1.并行和并发有什么区别?
  • 2.进程和线程的区别与联系?
  • 3.守护线程是什么?
  • 4.创建线程有哪几种方式?
  • 5.说一下 runnable 和 callable 有什么区别?
  • 6.线程有哪些状态?
  • 7.sleep() 和 wait() 有什么区别?
  • 8.notify()和 notifyAll()有什么区别?
  • 9.线程的 run() 和 start() 有什么区别?
  • 10.创建线程池有哪几种方式?
  • 11.Java线程池中submit() 和 execute()方法的区别
  • 12.在 java 程序中怎么保证多线程的运行安全?
  • 13.多线程锁的升级原理是什么?
  • 14. 什么是死锁?什么是活锁? 什么是线程饥饿?
  • 15.ThreadLocal 是什么?有哪些使用场景?
  • 16.说一下synchronized底层实现原理?
  • 17.synchronized和volatile的区别是什么?
  • 18.synchronized和Lock的区别是什么?
  • 19.说一下 你对atomic 的理解 ?
  • 20.说一下 你对Semaphore 的理解 ?

给个关注,再给一个小红心

下面是答案解析:

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

  • 并发:是指多个线程任务在同一个CPU上快速地轮换执行,由于切换的速度非常快,给人的感觉就是这些线程任务是在同时进行的,但其实并发只是一种逻辑上的同时进行;
  • 并行:是指多个线程任务在不同CPU上同时进行,是真正意义上的同时执行。

2.进程和线程的区别与联系?

区别
  • 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行。
  • 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
  • 系统开销:多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

联系
  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源;
  • 处理机分给线程,即真正在处理机上运行的是线程;
  • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

3.守护线程是什么?

  • 护线程是程序运行的时候在后台提供一种通用服务的线程。所有用户线程停止,进程会停掉所有守护线程,退出程序。
  • 守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点
应用场景:

JVM 中的垃圾回收线程就是典型的守护线程, 当 JVM 要退出时,垃圾回收线程也会结束自己的生命周期.

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

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口

4.通过线程池创建线程(ThreadPollExecutor,ExecutorService..)

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

  • Runnable和Callable 都是接口,分别提供run方法和call方法
  • Runnable的run方法无返回值,Callable的call方法提供返回值来表示任务运行结果
  • Runnable无法通过throws抛出异常,所有CheckedException必须在run方法内部处理。Callable可直接抛出Exception异常.
  • Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而Callable通过提交给线程池执行

6.线程有哪些状态?

这里要注意审题,是系统线程状态还是Java中线程的状态?

Java中线程的状态

java.lang.Thread类

代码语言:javascript
复制
public enum State {

    NEW,


    RUNNABLE,


    BLOCKED,

 
    WAITING,


    TIMED_WAITING,


    TERMINATED;
}
操作系统中线程的状态
  • 初始状态(NEW) 对应 Java中的NEW
  • 可运行状态(READY) 对应 Java中的 RUNNBALE 状态
  • 运行状态(RUNNING) 对应 Java中的 RUNNBALE 状态
  • 等待状态(WAITING) 该状态在 Java中被划分为了 BLOCKEDWAITINGTIMED_WAITING 三种状态 当线程调用阻塞式 API时,进程(线程)进入等待状态,这里指的是操作系统层面的。从 JVM层面来说,Java线程仍然处于 RUNNABLE 状态。 JVM 并不关心操作系统线程的实际状态,从 JVM 看来,等待CPU使用权(操作系统状态为可运行态)与等待 I/O(操作系统处于等待状态)没有区别,都是在等待某种资源,所以都归入RUNNABLE 状态
  • 终止状态 (DEAD) 对应 TERMINATED

7.sleep() 和 wait() 有什么区别?

sleep()和wait()都是线程暂停执行的方法。

  • sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
  • sleep()不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复
  • wait() 用于线程间的通信,调用时会放弃对象锁**,进入**等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备重新获得对象锁进入运行状态。
  • wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
  • sleep()方法必须捕获异常InterruptedException,而wait()\notify()以及notifyAll()不需要捕获异常。

8.notify()和 notifyAll()有什么区别?

  • notify() 方法随机唤醒对象的等待池中的一个线程,进入锁池
  • notifyAll() 唤醒对象的等待池中的所有线程,进入锁池
  • 等待池:假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,并进入该对象的等待池,等待池中的线程不会去竞争该对象的锁。
  • 锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待

9.线程的 run() 和 start() 有什么区别?

  • 调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();
  • 调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
  • 一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;而run() 方法没有限制。

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

  • 通过Executors工厂方法创建 (阿里巴巴开发规约中不建议使用此种方式创建线程池)
  • 通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue) 自定义创建(推荐)

11.Java线程池中submit() 和 execute()方法的区别

两个方法都可以向线程池提交任务

  • execute()方法的返回类型是void,它定义在Executor接口中。
  • submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。

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

程序中保证多线程运行安全的方式:

1.使用安全类,比如 Java. util. concurrent 下的类。

2.使用自动锁 synchronized。

3.使用手动锁 Lock 例如Reentrantlock。

4.保证一个或者多个操作在CPU执行的过程中不被中断。(原子性)

5.保证一个线程对共享变量的修改,另外一个线程能够立刻看到。(可见性)

6.保证程序执行的顺序按照代码的先后顺序执行。(有序性)

注意回答中不能缺少这3种特性

线程的安全性问题体现在:

  • 原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性
  • 可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行

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

锁的级别从低到高:

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

锁分级别原因:

没有优化以前,synchronized是重量级锁(悲观锁),使用 wait 和 notify、notifyAll 来切换线程状态非常消耗系统资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM 对 synchronized 关键字进行了优化,把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。

在学习并发编程知识synchronized时,我们总是难以理解其实现原理,因为偏向锁、轻量级锁、重量级锁都涉及到对象头,所以了解java对象头是我们深入了解synchronized的前提条件.这篇文章包含了对象头的解析以及锁膨胀过程的解析:

JAVA对象布局之对象头(Object Header)

14. 什么是死锁?什么是活锁? 什么是线程饥饿?

JAVA并发之加锁导致的活跃性问题剖析

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

1.ThreadLocal 介绍

2.ThreadLocal 应用

3.ThreadLocal 源码解析

3.1解决 Hash 冲突

4.ThreadLocal 特性

5..ThreadLocal 内存泄露问题

java并发之无同步方案-ThreadLocal

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

前置知识,需要了解 对象头.-->JAVA对象布局之对象头(Object Header)

  • 同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权 monitorenter,如果当前monitor的进入数为0时,线程就会进入monitor,并且把进入数+1,那么该线程就是monitor的拥有者(owner)。 如果该线程已经是monitor的拥有者,又重新进入,就会把进入数再次+1。也就是可重入的。 执行monitorexit的线程必须是monitor的拥有者,指令执行后,monitor的进入数减1,如果减1后进入数为0,则该线程会退出monitor。其他被阻塞的线程就可以尝试去获取monitor的所有权。 monitorexit指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;
  • 同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制 标志位ACC_SYNCHRONIZED,作用就是一旦执行到这个方法时,就会先判断是否有标志位,如果有这个标志位,就会先尝试获取monitor,获取成功才能执行方法,方法执行完成后再释放monitor。在方法执行期间,其他线程都无法获取同一个monitor。归根结底还是对monitor对象的争夺,只是同步方法是一种隐式的方式来实现。

总的来说,synchronized的底层原理是通过monitor对象来完成的

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

作用:

  • synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
  • volatile 表示变量在 CPU 的寄存器中是不确定的,必须从主存中读取。保证多线程环境下变量的可见性;禁止指令重排序。

区别:

  • synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
  • synchronized 可以保证线程间的有序性、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
  • synchronized 线程阻塞,volatile 线程不阻塞。

18.synchronized和Lock的区别是什么?

在多线程情况下,锁是线程控制的重要途径。Java为此也提供了2种锁机制,synchronized和lock。

我们这里不讨论具体的实现原理和细节,只讨论它们的区别

如果有小伙伴有兴趣更深入了解它们,请关注公众号:JAVA宝典

区别

  • lock是一个接口,而synchronized是java的一个关键字。
  • synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生(也称隐式锁和显式锁)
  • lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
  • Lock可以通过trylock来知道有没有获取锁,而synchronized不能;
  • Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)
  • 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要优于synchronized。在使用时要根据适当情况选择。
  • synchronized 是 JVM 层面实现的;Lock 是 JDK 代码层面实现
  • synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度
  • 继上一条,synchronized只有一个阻塞队列,而Lock使用Condition可以有多个阻塞队列 synchronized和lock的用法区别
    • synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
    • lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。

19.说一下 你对atomic 的理解 ?

在JDK5.0之前,想要实现无锁无等待的算法是不可能的,除非用本地库,自从有了Atomic变量类后,这成为可能。

在java.util.concurrent.atomic包下有这些类:

  • 标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • 数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
  • 复合变量类:AtomicMarkableReference,AtomicStampedReference

拿AtomicInteger来举例,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。

代码语言:javascript
复制
/** 
 * Atomically increments by one the current value. 
 * 
 * @return the previous value 
 */  
public final int getAndIncrement() {  
    return unsafe.getAndAddInt(this, valueOffset, 1);  
} 

这里直接调用一个叫Unsafe的类去处理,这个类是用于执行低级别、不安全操作的方法集合。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己的java程序中直接使用该类,因为只有授信的代码才能获得该类的实例。所以我们平时的代码是无法使用这个类的,因为其设计的操作过于偏底层,如若操作不慎可能会带来很大的灾难,所以直接禁止普通代码的访问,当然JDK使用是没有问题的。

关于CAS 在我的另一篇文章: 什么是CAS,ABA问题怎么解决?

20.说一下 你对Semaphore 的理解 ?

  1. Semaphore就是一个信号量,它的作用是限制某段代码块的并发数
  2. Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问
  3. 如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入
  4. 由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。

Semaphore类位于java.util.concurrent包下,它提供了2个构造器:

代码语言:javascript
复制
//参数permits表示许可数目,即同时可以允许多少线程进行访问  
public Semaphore(int permits) {  
    sync = new NonfairSync(permits);  
}  
//这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可  
public Semaphore(int permits, boolean fair) {  
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);  
}  
  • Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:
  • acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
  • release()用来释放许可。注意,在释放许可之前,必须先获获得许可。
代码语言:javascript
复制
//尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false  
public boolean tryAcquire() { };  
//尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false  
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };   
//尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false  
public boolean tryAcquire(int permits) { };   
//尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true  
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { };  
//得到当前可用的许可数目  
public int availablePermits(); 
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java宝典 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.并行和并发有什么区别?
  • 2.进程和线程的区别与联系?
    • 区别
      • 联系
      • 3.守护线程是什么?
        • 应用场景:
        • 4.创建线程有哪几种方式?
        • 5.说一下 runnable 和 callable 有什么区别?
        • 6.线程有哪些状态?
          • Java中线程的状态
            • 操作系统中线程的状态
            • 7.sleep() 和 wait() 有什么区别?
            • 8.notify()和 notifyAll()有什么区别?
            • 9.线程的 run() 和 start() 有什么区别?
            • 10.创建线程池有哪几种方式?
            • 11.Java线程池中submit() 和 execute()方法的区别
            • 12.在 java 程序中怎么保证多线程的运行安全?
            • 13.多线程锁的升级原理是什么?
            • 14. 什么是死锁?什么是活锁? 什么是线程饥饿?
            • 15.ThreadLocal 是什么?有哪些使用场景?
            • 16.说一下synchronized底层实现原理?
            • 17.synchronized和volatile的区别是什么?
            • 18.synchronized和Lock的区别是什么?
            • 19.说一下 你对atomic 的理解 ?
            • 20.说一下 你对Semaphore 的理解 ?
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档