Java面试题

ArrayList和LinkedList的区别

  • ArrayList: 基于动态数组,连续的内存存储,适合下标访问(随机访问)。扩容机制:长度固定,超出长度内存数组时新建数组,将原数组中的数据拷贝到新数组,如果不是尾部插入数据还会设计到元素的移动。
  • LinkedList: 基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询,因为查询需要逐一遍历
    遍历LinkedList必须使用iterator,不能使用for循环,因为每次for循环内部通过get(i)获取元素时需要对list重新进行遍历,消耗大
    不要试图使用indexOf返回元素索引,并利用其进行遍历,使用indexOf对list进行遍历,当结果为空时会遍历整个列表

ThreadLocal

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

线程安全的理解

当多个线程访问同一个对象时,如果不用进行额外的同步控制或其他协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的

是进程和线程共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程进行初始化的时候分配,运行过程中也可以向系统要额外的对,用完之后需要还给操作系统,否则就会内存泄漏

在Java中,堆是Java虚拟机所管理的内存中最大的一块,是所有线程共享的一块内存区域,在虚拟机启动时创建。堆所存在的内存区唯一的目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存

是每个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立

Java死锁

形成死锁的原因:

  • 一个资源每次只能被一个线程使用
  • 一个线程在阻塞等待某个资源时,不释放已占有资源
  • 一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
  • 若干线程形成头尾相接的循环等待资源关系

如何避免死锁:

  • 注意加锁顺序,保证每个线程按同样的顺序进行加锁
  • 注意加锁时限,可以针对锁设置一个超时时间
  • 注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决

提交任务时,线程池队列满,会发生什么

  • 如果使用的是无界队列LinkedBlockingQueue,那么可以继续提交任务
  • 如果使用的是有界队列比如 ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue 中ArrayBlockingQueue 满了,会根据maximumPoolSize 的值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockingQueue 继续满,那么则会使用拒绝策略,RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy

Synchronized和ReentrantLock区别

SynchronizedReentrantLock
关键字
自动加锁和释放锁手动加锁和释放锁
JVM层面的锁API层面的锁
非公平锁可以选择公平或者非公平
锁的是对象,锁信息保存在对象头中锁的是线程,通过代码中int类型的state标识来标识锁的状态
底层有一个锁升级的过程

线程池的作用以及参数

作用:

  1. 降低资源消耗;提高线程的利用率,降低创建和销毁线程的消耗
  2. 提高响应速度,任务来了,有线程可用可执行,而不是先创建线程,再执行
  3. 提高线程的可管理性;使用线程池可以统一分配调优监控

参数:

  • corePoolSize: 核心线程数,正常情况下创建工作的线程数,这些线程创建后并不会消除,而是一种常驻线程
  • maxinumPoolSize: 代表最大线程数,与核心线程相对应,标识最大允许被创建的线程数,比如当前任务较多,核心线程数用完了,无法满足需求时,就会创建新的线程,但是线程池内的总数不会超过最大线程数
  • keepAliveTime、unit: 表示超出核心线程数之外的线程空闲存活时间,核心线程不会消除,但超出核心线程数部分的线程如果空闲一定时间则会被消除,可以通过setKeepAliveTime来设置空间时间
  • workQueue: 用来存放待执行的任务,假设核心线程都已被使用,还有任务进来则全部放入队列,直到整个队列被放满但任务还再持续进入则开始创建新的线程
    ① ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
    ②LinkedBlockingQuene:基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
    ③SynchronousQuene:一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
    ④PriorityBlockingQueue:具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  • ThreadFactory: 实际上是一个线程工厂,用来生产线程执行任务。
  • handler :任务拒绝策略,两种情况,一:当调用shutdown等方法关闭线程池后,这时候即使线程池内部还有没执行完的任务正在进行,由于线程池关闭,再继续提交任务会遭到拒绝;二:另一种情况是当达到最大线程数,线程池没有能力继续处理新提交的任务时。
    ①CallerRunsPolicy:在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
    ②AbortPolicy: 直接丢弃任务,并抛出RejectedExecutionException异常。
    ③DiscardPolicy: 直接丢弃任务,什么都不做。
    ④DiscardOldestPolicy: 抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

CountDownLatch和Semaphore的区别和底层原理

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

AQS如何实现可重入锁

AQS:AbstractQueuedSynchronizer是并发容器J.U.C(java.util.concurrent)下locks包内的一个类。它实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列。底层实现的数据结构是一个双向链表。

  • 在AQS中,维护了一个信号量state和一个线程组成的双向链表队列,其中,这个线程队列,就是用来给线程排队的,而state就是用来控制线程排队或放行的
  • 在可重入锁场景下,state用来标识加锁的次数,0表示无锁,加一次锁,state就加1,释放锁就减1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值