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

Java经典面试题11-20

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

Java经典面试题11-20

(11)ReentrantLock中的公平锁和非公平锁的底层实现

首先不管是公平锁和非公平锁,它们的底层实现都会使用AQS来进行排队,它们的区别在于:

  1. 线程在使用lock()方法加锁时,如果是公平锁,会先检查AQS队列是否存在线程在排队,
  2. 如果有线程在排队,则当前线程也进行排队,
  3. 如果是非公平锁,则不会去检查是否有线程在排队,而是直接竞争锁

       不管是公平锁还是非公平锁,一旦没竞争到锁,都会进行排队,当锁释放时,都是唤醒排在最前面的线程,所以非公平锁只是体现在了线程加锁阶段,而没有体现在线程唤醒阶段。
另外,ReentranLock是可重入锁,不管是公平锁还是非公平锁都是可重入的。

 

 

package com.Test0721;

import java.util.concurrent.locks.ReentrantLock;

public class Test11 {

    static ReentrantLock reentrantLock = new ReentrantLock();//默认都是非公平锁

    public static void main(String[] args) {
        reentrantLock.lock();
        reentrantLock.lock();//加锁两次

        reentrantLock.unlock();
        reentrantLock.unlock();//解锁两次

    }
}


(12)ReentrantLock中tryLock()和Lock()方法的区别
  1. tryLock()表示尝试加锁,可能加到,也可能加不到,该方法不会阻塞线程,如果加到锁则返回true,没有加到锁则返回false
  2. lock()表示阻塞加锁,线程会阻塞直到加锁,方法也没有返回值
package com.Test0721;

import java.util.concurrent.locks.ReentrantLock;

import static com.Test0721.Test11.reentrantLock;

//ReentrantLock中tryLock()和Lock()方法的区别
public class Test12 {

    static ReentrantLock renntrantLock = new ReentrantLock();

    public static void main(String[] args) {

        renntrantLock.lock();//阻塞加锁,如果没获得到锁的话,他不会继续执行下面的其他代码,没有返回值
        //其他代码

        boolean result = reentrantLock.tryLock();//非阻塞锁,不管加没加到锁,都会有返回值boolean,没有就返回false

        //自选锁
        while (!reentrantLock.tryLock()){
            //其他事情
        }
//        其他代码

    }
}

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

import java.util.concurrent.Semaphore;

public class Test13 {

    static Semaphore semaphore = new Semaphore(3);

    public static void main(String[] args) throws InterruptedException {

        //线程1
        semaphore.acquire();

        //线程2
        semaphore.acquire();

        //线程3
        semaphore.acquire();

        //线程4
        semaphore.acquire();//阻塞

        //线程1
        semaphore.release();

    }
}


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


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


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

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


(17)JVM中哪些是线程共享区

        堆区和方法区是所有线程共享的,栈、本地方法栈、程序计数器是每个线程独有的


 

(18)JVM中哪些可以作为gc root

        什么是gc root,JVM在进行垃圾回收时,需要找到“垃圾”对象,也就是没有被引用的对象,但是直接找“垃圾”对象是比较耗时的,所以反过来,先找“非垃圾”对象,也就是正常对象,那么就需要从某些“根”的引用路径找到正常对象,而这些“根”有一个特征,就是它只会引用其他对象

例如:栈中的本地变量、方法区的静态变量、本地方法栈中的变量、正常运行的线程等可以作为gc root。


(19)你们项目如何排查JVM问题

对于还在正常运行的系统:

  1.  可以使用jmap来查看JVM中各个区域的使用情况
  2.  可以通过jstack来查看线程的运行情况,比如哪些线程阻塞、是否出现了死锁
  3.  可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc比较频繁,那么就得进⾏调优了
  4.  通过各个命令的结果,或者jvisualvm等工具来进行分析
  5.  首先,初步猜测频繁发送fullgc的原因,如果频繁发生fullgc但是又⼀直没有出现内存溢出,那么表示fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这些对象进⼊到老年代,对于这种情况,就要考虑这些存活时间不长的对象是不是比较大,导致年轻代放不下,直接进入到了老年代,尝试加大年轻代的大小,如果改完之后,fullgc减少,则证明修改有效
  6.  同时,还可以找到占用CPU最多的线程,定位到具体的方法,优化这个方法的执行,看是否能避免某些对象的创建,从而节省内存


(20)说说类加载器双亲委派模型

JVM中存在三个默认的类加载器:

  1.     BootstrapClassLoader
  2.     ExtClassLoader
  3.     AppClassLoader
  •      AppClassLoader的父加载器是ExtClassLoader
  •      ExtClassLoader的父加载器是BootstrapClassLoader。

        JVM在加载⼀个类时,会调用AppClassLoader的loadClass方法来加载这个类,不过在这个方法中,会先使用ExtClassLoader的loadClass方法来加载类,同样ExtClassLoader的loadClass方法中,会先使用BootstrapClassLoader来加载类,如果BootstrapClassLoader加载到了就直接成功,如果BootstrapClassLoader没有加载到,那么ExtClassLoader就会自己尝试加载该类,如果没有加载到,那么则会由AppClassLoader来加载这个类。

        所以,双亲委派指得是,JVM在加载类时,会委派给Ext和Bootstrap进行加载,如果没加载到才由自己进行加载。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1023757.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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