栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA面试备战--多线程篇

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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指令

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

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

锁的状态是存放在对象头的MarkWord上的;锁的标志位。

6.ReentrantLock和synchronize区别
  1. synchronize是java关键字,ReentrantLock是一个类
  2. synchronize会自动加锁和释放锁,ReentrantLock需要手动加解锁
  3. synchronize底层是jvm层面的锁,ReentrantLock是API层面的锁
  4. synchronize锁的是对象,锁信息保存在对象头上,ReentrantLock是通过代码中int类型的state标识来标识锁的状态
  5. 都属于可重入锁,synchronize是非公平锁,ReentrantLock默认非公平,但可以选择公平锁
  6. synchronize底层有所升级过程
7. CountDownLatch和Semaphore

CountDownLatch表示计数器,一般给CountDownLatch设置一个数字,一个线程调用CountDownLatch的await()方法将会阻塞当前线程,其他线程可以调用CountDownLatch的countDown()方法来对CountDownLatch中的数字减一,当数字被剪成0后,所有await的线程都将被唤醒。底层实现的话,调用await()方法会利用AQS排队,一旦数字被减为0,则会将AQS中排队的线程依次唤醒。

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

8. 线程池底层工作原理

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

  1. 如果此时线程池中的线程数量笑愚corePoolSize,即线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务
  2. 如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列
  3. 如果此时线程池中的线程数量大于等于corePoolSize,缓冲队列workQueue满,兵器线程池中的数量笑愚maxnumPoolSize,建新的线程来处理被添加的任务
  4. 如果此时线程池中的线程数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,那么通过handler所制定的策略来处理此任务
  5. 当线程池中的线程数量大于corePoolSize时,如果某空闲时间超过KeepAliveTime,线程将被终止,这样,线程池可以动态调整池中的线程数。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1031944.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号