1 Star 0 Fork 31

阿明 / Ebooks

forked from Java精选 / Ebooks 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
最新2021年Java并发面试题及答案汇总版.md 12.37 KB
一键复制 编辑 原始数据 按行查看 历史

最新2021年Java并发面试题及答案汇总版

全部面试题答案,更新日期:01月30日,直接下载吧!

下载链接:高清500+份面试题资料及电子书,累计 10000+ 页大厂面试题 PDF

Java 并发

题1:Java 中 AQS 核心思想是什么?

AQS核心思想是如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用。

题2:为什么调用start()方法时需执行run()方法?

当程序调用start()方法时将创建新的线程且执行在run()方法中的代码。

如果直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码,只会把run方法当作普通方法去执行。

题3:什么是原子操作?

原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。

在Java中可以通过锁和循环CAS的方式来实现原子操作。 CAS操作——Compare And Set,或是Compare And Swap,现在几乎所有的CPU指令都支持CAS的原子操作。

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。

题4:锁优化的方法有哪些?

减少锁持有时间

减少其他线程等待的时间,只在有线程安全要求的程序代码上加锁。

减小锁粒度

将大对象(这个对象可能会被很多线程访问),拆成小对象,大大增加并行度,降低锁竞争。降低了锁的竞争,偏向锁,轻量级锁成功率才会提高。

锁分离

读写锁ReadWriteLock,根据功能进行分离成读锁和写锁,这样读读不互斥,读写互斥,写写互斥。即保证了线程安全,又提高了性能。

读写分离思想可以延伸,只要操作互不影响,锁就可以分离。

锁粗化

为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早的获得资源执行任务。

锁消除

在即时编译器时,如果发现不可能被共享的对象,则可以消除这些对象的锁操作。

题5:什么是线程安全?

如果代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的且其他变量的值也和预期的是一样的,就是线程安全的。

或者也可以理解成一个类或程序所提供的接口对于线程来说是原子操作,多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说不用考虑同步的问题,那就是线程安全的。

题6:Java 中如何唤醒一个阻塞的线程?

如果线程是因调用wait()、sleep()或join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

以下是详细的唤醒方法:

1、sleep()方法

sleep(毫秒),指定以毫秒为单位的时间,使线程在该时间内进入线程阻塞状态,期间得不到cpu的时间片,等到时间过去了,线程重新进入可执行状态。(暂停线程,不会释放锁)

2、suspend()和resume()方法

挂起和唤醒线程,suspend e()使线程进入阻塞状态,只有对应的resume()被调用的时候,线程才会进入可执行状态。(不建议用,容易发生死锁)

3、yield()方法

会使的线程放弃当前分得的cpu时间片,但此时线程任然处于可执行状态,随时可以再次分得cpu时间片。yield()方法只能使同优先级的线程有执行的机会。调用

yield()的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。(暂停当前正在执行的线程,并执行其他线程,且让出的时间不可知)

4、wait()和notify()方法

两个方法搭配使用,wait()使线程进入阻塞状态,调用notify()时,线程进入可执行状态。wait()内可加或不加参数,加参数时是以毫秒为单位,当到了指定时间或调用notify()方法时,进入可执行状态。(属于Object类,而不属于Thread类,wait()会先释放锁住的对象,然后再执行等待的动作。由于wait()所等待的对象必须先锁住,因此,它只能用在同步化程序段或者同步化方法内,否则,会抛出异常IllegalMonitorStateException.)

5、join()方法

也叫线程加入。是当前线程A调用另一个线程B的join()方法,当前线程转A入阻塞状态,直到线程B运行结束,线程A才由阻塞状态转为可执行状态。

以上是Java线程唤醒和阻塞的五种常用方法,不同的方法有不同的特点,其中wait()和notify()是其中功能最强大、使用最灵活的方法,但这也导致了它们效率较低、较容易出错的特性,因此,在实际应用中应灵活运用各种方法,以达到期望的目的与效果。

题7:Java 中用到的线程调度算法是什么?

计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。在运行池中,会有多个处于就绪状态的线程在等待CPU,JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权。

有两种调度模型:分时调度模型和抢占式调度模型。

分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片这个也比较好理解。

java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。

题8:公平锁和非公平锁有什么区别?

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

优点:所有的线程都能得到资源,不会饿死在队列中。

缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。

缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

题9:常用的并发工具类有哪些?

CountDownLatch闭锁

CountDownLatch是一个同步计数器,初始化时传入需要计数线程的等待数,可能是等于或大于等待执行完的线程数。调用多个线程之间的同步或说起到线程之间的通信(不是互斥)一组线程等待其他线程完成工作后在执行,相当于加强的join。

CyclicBarrier栅栏

CyclicBarrier字面意思是栅栏,是多线程中重要的类,主要用于线程之间互相等待的问题,初始化时传入需要等待的线程数。

作用:让一组线程达到某个屏障被阻塞直到一组内最后一个线程达到屏蔽时,屏蔽开放,所有被阻塞的线程才会继续运行。

Semophore信号量

semaphore称为信号量是操作系统的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。

作用:semaphore管理一系列许可每个acquire()方法阻塞,直到有一个许可证可以获得,然后拿走许可证,每个release方法增加一个许可证,这可能会释放一个阻塞的acquire()方法,然而并没有实际的许可保证这个对象,semaphore只是维持了一个可获取许可的数量,主要控制同时访问某个特定资源的线程数量,多用在流量控制。

Exchanger交换器

Exchange类似于交换器可以在队中元素进行配对和交换线程的同步点,用于两个线程之间的交换。

具体来说,Exchanger类允许两个线程之间定义同步点,当两个线程达到同步点时,它们交换数据结构,因此第一个线程的数据结构进入到第二个线程当中,第二个线程的数据结构进入到第一个线程当中。

题10:多线程实现的方式有几种?

1)继承Thread类创建线程

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。

启动线程的唯一方法是通过Thread类的start()实例方法。

start()方法将启动一个新线程,并执行run()方法。

这种方式实现多线程比较简单,通过自己的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。

2)实现Runnable接口创建线程

如果自己的类已经继承了两一个类,就无法再继承Thread,因此可以实现一个Runnable接口

3)实现Callable接口,通过FutureTask包装器来创建Thread线程

4)使用ExecutorServiceCallableFuture实现有返回结果的线程

ExecutorServiceCallableFuture三个接口实际上都是属于Executor框架。

在JDK1.5中引入的新特征,不需要为了得到返回值而大费周折。主要需要返回值的任务必须实现Callable接口;不需要返回值的任务必须实现Runnabel接口。

执行Callable任务后,可以获取一个Future对象,在该对象上调用get()方法可以获取到Callable任务返回的Object了。注意的是get()方法是阻塞的,线程没有返回结果时,该方法会一直等待。

题11:什么是并发容器的实现

题12:java-中-volatile-和-synchronized-有什么区别

题13:什么是线程

题14:什么是竞争条件如何发现和解决竞争

题15:copyonwritearraylist-可以用于什么应用场景

题16:如何保证运行中的线程暂停一段时间

题17:java-中为什么代码会重排序

题18:什么是线程组为什么-java不推荐使用

题19:java-中线程阻塞都有哪些原因

题20:什么是-threadlocal-变量

题21:java-中的-readwritelock-是什么

题22:如何避免线程死锁

题23:java-中--操作符是线程安全的吗

题24:什么是-java-内存模型

题25:java-线程池中-submit()-和-execute()-方法有什么区别

大厂面试题

大厂面试题

大厂面试题

Java
1
https://gitee.com/AminDev/ebooks.git
git@gitee.com:AminDev/ebooks.git
AminDev
ebooks
Ebooks
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891