面试题(线程并发篇)

CountDownLatch和Semaphore的区别和底层原理

CountDownLatch表示计数器,可以给CountDownLatch设置一个数字,一个线程调用CountDownLatch的await()将会阻塞,其他线程可以调用CountDownLatch的countDown()方法对CountDownLatch中的数字减一,数字减成0之后,所有await的线程都将被唤醒。对应的底层原理是,调用await()方法的线程会利用AQS排队,数字减为0,则会将AQS中排队的线程依次唤醒。

Semaphore表示信号量,可以设置许可个数,表示同时允许最多多少个线程使用该信号量,通过acquire()来获取许可,如果没有许可可用则线程阻塞,通过AQS来排队,可以通过release()方法释放许可,会从AQS中排队的第一个线程开始依次唤醒。 

ReentrantLock中的tryLock()和lock()方法的区别

lock()方法没有返回值,阻塞加锁

trylock()方法有返回值(boolean),非阻塞加锁

如何查看线程死锁

通过jstack命令来进行查看,jstack命令中会显示发生死锁的线程

两个线程去操作数据库,数据库发生了死锁,可以查询数据库的死锁情况

ThreadLocal的原理和使用场景

每一个Thread对象均含有一个ThreadLocalMap类型的成员变量threadLocals(key),它存储本线程中所有ThreadLocal对象及其对应的值(value(线程变量)), 是一个弱引用,如果没有指向key的强引用后,key就会被垃圾收集器回收,执行set方法时,ThreadLocal首先获取当前线程对象,然后获取当前线程的ThreadLocalMap对象,以当前ThreadLocal对象为key,将值存储进ThreadLocalMap对象中。执行get方法时,ThreadLocal首先获取当前线程对象,然后获取当前线程的ThreadLocalMap对象,以当前ThreadLocal对象为key,获取对应的value。每一个线程都有私有的ThreadLocalMap容器,这些容器互相独立互不影响,不会存在线程的安全问题,无需同步机制来保证多条线程访问容器的互斥性。

使用场景:进行对象跨层传递时,使用ThreadLocal避免多次传递,打破层次间的约束;线程间数据隔离,进行事务操作,存储线程事务信息,数据库连接Session会话管理,Spring在事务开始时会给当前线程绑定一个Jdbc Connection,用的就是ThreadLocal来实现这种隔离。

Sychronized的偏向锁、轻量级锁、重量级锁

偏向锁:在锁对象的对象头记录当前获取到该锁的线程ID,该线程下次如果又来获取锁就可以直接获取到。

轻量级锁:当一个线程获取锁后,此时这把锁时偏向锁,此时如果有第二个线程来竞争锁,偏向锁就会升级成轻量级锁,轻量级锁底层通过自旋来实现,不会阻塞线程。

重量级锁:如果自旋次数过多仍然没有获取到锁,则会升级成重量级锁,重量级锁会导致线程阻塞。

自旋锁:自旋锁就是线程在获取锁的过程中,不回去阻塞线程,也就无所谓唤醒线程,阻塞和唤醒都需要操作系统去进行的,比较消耗时间,自旋锁是通过CAS获取预期的一个标记,如果没有获取到,继续循环获取,如果获取到了表示获取到了锁,这个过程线程一直在运行中,相对而言没有使用太多的操作系统资源。

优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距如果线程间存在锁竞争,会带来额外的锁撤销的消耗适用于只有一个线程访问同步块场景
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果始终得不到锁竞争的线程使用自旋会消耗CPU追求响应时间,锁占用时间很短
重量级锁线程竞争不使用自旋,不会消耗CPU线程阻塞,响应时间缓慢追求吞吐量,锁占用时间较长

Sychronized和ReentrantLock的区别

sychronized是一个关键字,ReentrantLock是一个类;sychronized会自动加锁和释放锁,ReentrantLock需要手动加锁和释放锁;sychronized的底层是JVM层面的锁,ReentrantLock的底层是API层面的锁;sychronized是非公平锁,ReentrantLock可以选择公平锁或非公平锁;sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识来标识锁的状态;sychronized底层有一个锁升级的过程。

并发、并行、串行的区别

串行在时间上不可能发生重叠,前一个任务没完成,后一个任务只能等待。

并行在时间上是重叠的,两个任务在同一时刻互不干扰的同时执行。

并发允许两个任务彼此干扰,同一时间点,只有一个任务运行,交替执行。

并发的三大特性

原子性:在一个操作中CPU不可以中途暂停然后再调度,即不被中断操作,要不全部执行完,要不都不执行。synchronized,Lock,Atomic。

可见性: 当一个线程修改了共享变量的值,其他线程都能看到修改的值。volatile,synchronized,Lock,Atomic,final。

有序性:程序的执行顺序按照代码的先后顺序,synchronized,Lock。

线程之间如何进行通讯

线程之间可以通过共享内存或基于网络来进行通信;通过共享内存来进行通信,需要考虑并发问题,什么时候阻塞,什么时候唤醒,wait()、notify()就是阻塞和唤醒;通过网络连接将通信数据发送给对方,也要考虑并发问题,处理的方式就是用加锁等。

对线程安全的理解

不是线程问题,应该是内存安全,堆是共享内存,可以被所有线程访问,当多个线程访问一个对象时,不用进行额外的操作,调用这个对象的行为都可以获得正确的结果(跟单个线程执行的结果一样),我们就说这个对象时线程安全的。 

简述线程池流程

创建若干个可执行的线程放入一个池中,有任务需要处理时,提交到线程池的任务队列,处理完之后线程不会销毁,而是在线程池中等待下一个任务。降低资源消耗,提高响应速度,提高线程的可管理性。

excute向线程池中提交任务→核心线程→阻塞队列→非核心线程→RejectExecutionException。

核心线程:向线程池提交任务时,首先创建核心线程运行任务,直到核心线程数达到上限,将任务放到阻塞队列。

非核心线程:只有在核心线程数达到上限,且阻塞队列满的情况下,才会创建非核心线程运行任务。

线程池底层工作原理

线程池内部通过队列+线程实现的,当我们利用线程执行任务时:

首先创建核心线程运行任务,直到核心线程数达到上限,将任务放到阻塞队列,阻塞队列满的情况下,创建非核心线程运行任务。如果核心线程数达到上限,阻塞队列满,池中的数量等于最大线程数(maximumPoolSize),通过拒绝处理任务的策略(handler)指定的策略来处理此任务。线程池中的数量大于核心线程数时,如果某线程空闲时间超过等待时间(keepAliveTime),线程将被终止。动态调整线程池中的线程数。

线程的生命周期和状态

创建、就绪、运行、阻塞、死亡

阻塞:(1)等待阻塞:运行的线程执行wait方法,该线程会释放占有的资源,JVM会把线程放入"等待池",这个状态无法自动唤醒,需要notify或notifyAll唤醒,wait是Object类的方法。(2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入"锁池"中。(3)其他阻塞:运行sleep或者join方法,或者发出I/O请求时,JVM会把该线程设置为阻塞状态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态,sleep是Thread类的方法。

新建:新创建一个线程对象;就绪:线程创建后调用该线程的start方法。该状态的线程位于可运行线程池中,等待获取CPU的使用权;运行:就绪状态获取了CPU,执行程序代码;阻塞:因为某种原因放弃CPU的使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态;死亡:线程执行完或因异常退出了run方法,该线程结束生命周期。

sleep(),wait(),join(),yield()的区别

1.锁池:所有需要竞争同步锁的线程都会放到锁池,当前对象锁被一个线程拿到后,其他线程都需要在锁池中等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列等到cpu分配资源。

2.等待池:当我们调用wait()方法,线程会放到等待池中,等待池的线程不会去竞争同步锁,只有调用notify()或notifyAll()后等待池才会开始去竞争锁,notify()是随机选取一个等待池的线程放到锁池,notifyAll()是将等待池中所有线程放到锁池中。

sleep()是Thread类的,不会释放锁,不需要依赖同步器synchronized,不需要被唤醒,用于当前线程休眠或暂停操作,会让出CPU执行时间且强制上下文切换,

wait()是Object类的,会释放锁,而且会加入等待队列,需要依赖synchronized,需要被唤醒,多用于多线程直接的通信,wait不一定会让出CPU执行时间,wait结束后还是可能重新竞争到锁继续执行。

yield()执行后线程直接进入就绪状态,马上释放CPU的执行权,但是依然保留CPU执行的资格,很有可能CPU下次进行线程调度还是会让这个线程拿到执行权继续执行。

join()执行后线程进入阻塞状态,例如在线程B中调用线程A的join(),那么线程B会进入阻塞,直到线程A结束或中断线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

J.k.x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值