I'm spiderman I'm spiderman
首页
  • 中间件
  • 基础架构
  • 微服务
  • 云原生
  • Java
  • Go
  • PHP
  • Python
  • 计算机网络
  • 操作系统
  • 数据结构
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档

spiderman

快乐学习,快乐编程
首页
  • 中间件
  • 基础架构
  • 微服务
  • 云原生
  • Java
  • Go
  • PHP
  • Python
  • 计算机网络
  • 操作系统
  • 数据结构
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
  • Java

    • Java基本原理
    • JVM原理
    • 线程
      • Spring原理
      • Mybatis和JPA
      • JVM调优
    • Golang

    • PHP

    • Python

    • 计算机语言
    • Java
    spiderman
    2022-12-20
    目录

    线程

    # Java 线程原理

    # 对线程安全的理解

    线程安全指的是,我们写的某段代码,在多个线程同时执⾏这段代码时,不会产⽣混乱,依然能够得到 正常的结果,⽐如i++,i初始化值为0,那么两个线程来同时执⾏这⾏代码,如果代码是线程安全的,那 么最终的结果应该就是⼀个线程的结果为1,⼀个线程的结果为2,如果出现了两个线程的结果都为1,则 表示这段代码是线程不安全的。 所以线程安全,主要指的是⼀段代码在多个线程同时执⾏的情况下,能否得到正确的结果。

    # 对守护线程的理解

    线程分为⽤户线程和守护线程,⽤户线程就是普通线程,守护线程就是JVM的后台线程,⽐如垃圾回收 线程就是⼀个守护线程,守护线程会在其他普通线程都停⽌运⾏之后⾃动关闭。我们可以通过设置 thread.setDaemon(true)来把⼀个线程设置为守护线程。

    # ThreadLocal的底层原理

    1. ThreadLocal是Java中所提供的线程本地存储机制,可以利⽤该机制将数据缓存在某个线程内部, 该线程可以在任意时刻、任意⽅法中获取缓存的数据
    2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对 象)中都存在⼀个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的 值
    3. 如果在线程池中使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要把 设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,⽽线程对象是通过强 引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收, Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿ 动调⽤ThreadLocal的remove⽅法,⼿动清楚Entry对象
    4. ThreadLocal经典的应⽤场景就是连接管理(⼀个线程持有⼀个连接,该连接对象可以在不同的⽅ 法之间进⾏传递,线程之间不共享同⼀个连接)

    # 并发、并⾏、串⾏之间的区别

    1. 串⾏:⼀个任务执⾏完,才能执⾏下⼀个任务
    2. 并⾏(Parallelism):两个任务同时执⾏
    3. 并发(Concurrency):两个任务整体看上去是同时执⾏,在底层,两个任务被拆成了很多份,然后 ⼀个⼀个执⾏,站在更⾼的⻆度看来两个任务是同时在执⾏的

    # Java死锁如何避免?

    造成死锁的⼏个原因:

    1. ⼀个资源每次只能被⼀个线程使⽤
    2. ⼀个线程在阻塞等待某个资源时,不释放已占有资源
    3. ⼀个线程已经获得的资源,在未使⽤完之前,不能被强⾏剥夺
    4. 若⼲线程形成头尾相接的循环等待资源关系 这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满⾜其中某⼀个条件即可。⽽其中前3 个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。 在开发过程中:
    5. 要注意加锁顺序,保证每个线程按同样的顺序进⾏加锁
    6. 要注意加锁时限,可以针对所设置⼀个超时时间
    7. 要注意死锁检查,这是⼀种预防机制,确保在第⼀时间发现死锁并进⾏解决

    # 线程池的底层⼯作原理

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

    1. 如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建 新的线程来处理被添加的任务。
    2. 如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放⼊ 缓冲队列。
    3. 如果此时线程池中的线程数量⼤于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数 量⼩于maximumPoolSize,建新的线程来处理被添加的任务。
    4. 如果此时线程池中的线程数量⼤于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等 于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
    5. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被 终⽌。这样,线程池可以动态的调整池中的线程数

    # 线程池为什么是先添加列队⽽不是先创建最⼤线程?

    当线程池中的核⼼线程都在忙时,如果继续往线程池中添加任务,那么任务会先放⼊队列,队列满了之 后,才会新开线程。这就相当于,⼀个公司本来有10个程序员,本来这10个程序员能正常的处理各种需 求,但是随着公司的发展,需求在慢慢的增加,但是⼀开始这些需求只会增加在待开发列表中,然后这 10个程序员加班加点的从待开发列表中获取需求并进⾏处理,但是某⼀天待开发列表满了,公司发现现 有的10个程序员是真的处理不过来了,所以就开始新招员⼯了。

    # ReentrantLock中的公平锁和⾮公平锁的底层实现

    ⾸先不管是公平锁和⾮公平锁,它们的底层实现都会使⽤AQS来进⾏排队,它们的区别在于:线程在使 ⽤lock()⽅法加锁时,如果是公平锁,会先检查AQS队列中是否存在线程在排队,如果有线程在排队, 则当前线程也进⾏排队,如果是⾮公平锁,则不会去检查是否有线程在排队,⽽是直接竞争锁。 不管是公平锁还是⾮公平锁,⼀旦没竞争到锁,都会进⾏排队,当锁释放时,都是唤醒排在最前⾯的线 程,所以⾮公平锁只是体现在了线程加锁阶段,⽽没有体现在线程被唤醒阶段。 另外,ReentrantLock是可重⼊锁,不管是公平锁还是⾮公平锁都是可重⼊的 lock1 lock2

    # ReentrantLock中tryLock()和lock()⽅法的区别

    1. tryLock()表示尝试加锁,可能加到,也可能加不到,该⽅法不会阻塞线程,如果加到锁则返回 true,没有加到则返回false
    2. lock()表示阻塞加锁,线程会阻塞直到加到锁,⽅法也没有返回值

    # CountDownLatch和Semaphore的区别和底层原理

    CountDownLatch表示计数器,可以给CountDownLatch设置⼀个数字,⼀个线程调⽤ CountDownLatch的await()将会阻塞,其他线程可以调⽤CountDownLatch的countDown()⽅法来对 CountDownLatch中的数字减⼀,当数字被减成0后,所有await的线程都将被唤醒。 对应的底层原理就是,调⽤await()⽅法的线程会利⽤AQS排队,⼀旦数字被减为0,则会将AQS中 排队的线程依次唤醒。 Semaphore表示信号量,可以设置许可的个数,表示同时允许最多多少个线程使⽤该信号量,通 过acquire()来获取许可,如果没有许可可⽤则线程阻塞,并通过AQS来排队,可以通过release()⽅法来释放许可,当某个线程释放了某个许可后,会从AQS中正在排队的第⼀个线程开始依次唤 醒,直到没有空闲许可。

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

    1. 偏向锁:在锁对象的对象头中记录⼀下当前获取到该锁的线程ID,该线程下次如果⼜来获取该锁就 可以直接获取到了
    2. 轻量级锁:由偏向锁升级⽽来,当⼀个线程获取到锁后,此时这把锁是偏向锁,此时如果有第⼆个 线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开来,轻 量级锁底层是通过⾃旋来实现的,并不会阻塞线程
    3. 如果⾃旋次数过多仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞
    4. ⾃旋锁:⾃旋锁就是线程在获取锁的过程中,不会去阻塞线程,也就⽆所谓唤醒线程,阻塞和唤醒 这两个步骤都是需要操作系统去进⾏的,⽐较消耗时间,⾃旋锁是线程通过CAS获取预期的⼀个标 记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程⼀直在运 ⾏中,相对⽽⾔没有使⽤太多的操作系统资源,⽐较轻量。

    # Sychronized和ReentrantLock的区别

    1. sychronized是⼀个关键字,ReentrantLock是⼀个类
    2. sychronized会⾃动的加锁与释放锁,ReentrantLock需要程序员⼿动加锁与释放锁
    3. sychronized的底层是JVM层⾯的锁,ReentrantLock是API层⾯的锁
    4. sychronized是⾮公平锁,ReentrantLock可以选择公平锁或⾮公平锁
    5. sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识 来标识锁的状态
    6. sychronized底层有⼀个锁升级的过程

    # 谈谈你对AQS的理解,AQS如何实现可重⼊锁?

    1. AQS是⼀个JAVA线程同步的框架。是JDK中很多锁⼯具的核⼼实现框架。
    2. 在AQS中,维护了⼀个信号量state和⼀个线程组成的双向链表队列。其中,这个线程队列,就是⽤ 来给线程排队的,⽽state就像是⼀个红绿灯,⽤来控制线程排队或者放⾏的。 在不同的场景下, 有不⽤的意义。
    3. 在可重⼊锁这个场景下,state就⽤来表示加锁的次数。0标识⽆锁,每加⼀次锁,state就加1。释 放锁state就减1。
    #线程
    JVM原理
    Spring原理

    ← JVM原理 Spring原理→

    最近更新
    01
    innovation create future
    12-13
    02
    RabbitMQ
    12-06
    03
    StarRocks的应用
    09-11
    更多文章>
    Theme by Vdoing | Copyright © 2022-2024 spiderman | 粤ICP备2023019992号-1 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式