JAVA面试备战--多线程篇

1. ThreadLocal原理

  • ThreadLocal是Java中所提供的的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程在任意时刻、任意方法中获取缓存的数据
  • 底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,value为需要缓存的值
  • 可能会存在内存泄露,因为当ThreadL对象使用完成之后,需要把key和value都回收掉,也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap的,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也不会被回收,从而出现内存泄露,解决方案就是,使用完ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清楚Entry对象
  • 使用场景大多数是连接管理,一个线程持有一个连接对象,该对象在不同的方法之间进行传递,线程之间不共享同一个连接

2. volatile关键字

  • 禁止指令重排
  • 保证属性线程之间可见性

volatile关键字来修饰对象的属性,在并发环境下保证属性的可见性,对于加了volatile关键字的属性,在对这个属性进行修改的时候,会直接将CPU缓存中的数据写会到主内存中,对于这个变量的读取也会直接从主内存中读取,从而保证了可见性,底层是通过操作系统的内存屏障来实现的,禁止指令重排,保证有序性。

3.几种锁概念介绍

这些都只是设计或者说概念上的东西,具体到JAVA上面的锁,可能属于其中的一种或者多种,比如synchronize属于可重入锁和非公平锁。

  • 公平锁:不直接尝试加锁,直接加入排队队列
  • 非公平锁 :先尝试加锁,失败了再加入排队队列
  • 可重入锁:同一个线程,多次尝试加锁,都能加锁成功,或者说在一个方法里面加了锁,调用了另外一个方法,尝试加同样的锁,也能加锁成功
  • 自旋锁:循环尝试去加锁,一直自旋…,不会阻塞线程
  • 偏向锁:一个线程加了锁后,后续这个线程再次加锁,可以直接获取到

4. ReentrantLock

默认非公平,底层实现都是使用AQS来进行排队,属于可重入锁。
公平锁和非公平锁区别:在使用lock()方法加锁时,如果是公平锁,会先检查AQS队列中是否有线程在排队,有的话直接加入到排队队列上,如果是非公平锁,则先去竞争锁,尝试获取锁,获取不到的时候再加入排队队列。
lock():阻塞加锁,等待加锁完成后,再执行后续代码;
tryLock():非阻塞加锁,tryLock();方法返回一个布尔值,不用非得等到加锁成功,可以继续执行其他代码,一般用于自旋锁,循环判断tryLock是否成功,然后再去做其他事情

5. synchronized

  • java关键字,可以修饰方法,变量,类等等。
  • 底层是通过CAS【比较并交换】的方式加锁,最底层汇编层面主要是lock cmxchg指令

synchronized锁升级机制:
无锁状态====》偏向锁====》轻量级锁====》重量级锁

  1. 一开始默认是没有锁状态的
  2. 偏向锁:一个线程进来之后加锁,先加偏向锁,这样该线程下次如果再次获取该锁就可以直接获取到了
  3. 轻量级锁:一个线程加上偏向锁后,如果此时有第二个线程来竞争所,偏向锁就会升级为轻量级锁,轻量级锁底层是通过锁自旋来实现的,并不会阻塞线程
  4. 如果自旋次数过多【默认10次】,则会升级为重量级锁(Mutex Lock),重量级锁会导致线程阻塞,重量级锁的话,对象关联的Monitor对象,加上重量级锁后,该对象头中的MarkWord设置指向Monitor对象的指针。重量级锁属于操作系统级别的锁,涉及到用户态和内核态的切换,成本比较高。

锁的状态是存放在对象头的MarkWord上的ÿ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值