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结束或中断线程。