多线程面试题

1 篇文章 0 订阅
1 篇文章 0 订阅

synchronize 底层原理

每个对象都有一个监视器锁(monitor),通过monitorEnter和monitorExist尝试获取锁和释放锁,当monitor的进入数为0时,通过monitorEnter指令尝试将进入数设置为1表示获取到锁,synchronize是可重入锁(double check的单例模式是最好证明)。

Lock基本思想

  • 个volatile类型变量用来表示锁的状态
  • 一个队列用来存储因未获取到锁而阻塞的线程

获取锁的流程

  • 读取锁状态
  • 若锁状态为0,通过CAS操作将锁状态置为1(多个状态只有一个会成功)
  • 成功获取到锁从队列中移除,将下一个节点设置为首节点
  • 若不成功继续留在队列中

释放锁的流程

  • 释放锁的线程将锁的状态由1置为0,并唤醒队列的中的首节点,从unlock方法返回,继续执行其他代码
  • 队列中的首节点和其他节点竞争,尝试获取锁

公平锁和非公平锁

公平锁是按照顺序获取锁,非公平锁可以插队获取锁,非公平锁效率高,非公平锁在获取锁是不会判断队列中是否为空,(前一个释放了锁,并唤醒了下一个线程,但下一线程并未从队列中移除)

volatile

volatile保证内存变量的可见行,保证单一读写操作的原子性,不能保证对j++符合操作的原子性,volatile禁止指令重排序
volatile的long或者double的变量读写是原子的,不然在多线程中可能只读取到改值的前32位

volatile和synchronize对比

volatile不会造成线程阻塞,synchronize 会造成线程阻塞
volatile保证可见行,synchronize保证可见性和原子性
volatile本质是在告诉jvm当前变量工作内存中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

synchronize和lock对比

两种锁的底层实现
Synchronized:底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0则释放锁。

Lock:底层是CAS乐观锁,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。
lock需要手动释放而synchronize自动释放

        try{
            lock.lock();
        }finally {
            lock.unlock();
        }

lock更灵活

synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)

线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待具体是使用tryLock(long timeout,TimeUnit unit)方法,在规定时间内获取到锁返回true,否则返回false
lock可以可以使用Interruptibly 中断,synchronize必须执行完毕后释放
Lock可以使用读锁提高多线程读效率

线程池

// Java线程池的完整构造函数
public ThreadPoolExecutor(
  int corePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
  int maximumPoolSize, // 线程数的上限,线程数超过corePoolSize,且排队队列已经满
  long keepAliveTime, TimeUnit unit, // 超过corePoolSize的线程的idle时长,
                                     // 超过这个时间,多余的线程会被回收,但线程数不会低于corePoolSize
  BlockingQueue<Runnable> workQueue, // 任务的排队队列
  ThreadFactory threadFactory, // 新线程的产生方式
  RejectedExecutionHandler handler) // 拒绝策略
  • workingQueue选择
    ArrayBlockingQueue;
    LinkedBlockingQueue;
    SynchronousQueue;

    • ArrayBlockingQueue内部是一个定长数组,有两个变量分别记录队列的头节点和为节点在数组中的位置。添加元素和移除元素使用的是同一个锁。
    • LinkedBlockingQueue 默认长度是Integer.Max 容易引起OOM,添加元素和移除元素使用的是独立的锁。
  • threadFactory 线程工程,用来创建线程

  • RejectedExecutionHandler 拒绝策略
    AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    也是丢弃任务,但是不抛出异常
    丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程
    丢弃最先进入的任务
    由调用线程处理该任务

  • corePoolSize核心线程数和最大线程数设置不当会影响效率

  • maximumPoolSize等待队列设置不当可能引发OOM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值