• Java面试——并发问题(sleep、synchronized、volatile等)



    并发

    线程有哪些状态

    在这里插入图片描述
    Java线程分成六种状态

    • 新建
    • 可运行
    • 阻塞
    • 等待
    • 等待(有时间)
    • 消亡

    在这里插入图片描述

    线程池的核心参数

    在这里插入图片描述

    1.corePoolSize 核心线程数目

    • 最多保留的线程数

    2.maximumPoolSize 最大线程数目

    • 核心线程+救急线程

    3.keepAliveTime 生存时间

    • 针对救急线程

    4.unit 时间单位

    • 针对救急线程

    5.workQueue

    • 阻塞队列

    6.threadFactory 线程工厂

    • 可以为线程创建时起名字

    7.handler 拒绝策略

    • 四种

    sleep VS wait

    • 共同点:wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态。
    • 方法归属不同
      • sleep方法是Thread的静态方法
      • 而wait(),wait(long)是Object的成员方法,每个对象都有
    • 醒来时间不同
      • 执行sleep(long)和wait(long)的线程都会在等待相同毫秒后醒来
      • wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒就一直等待下去
      • 都可以被打断唤醒
    • 锁特性不同
      • wait方法的调用必须先获取wait对象的锁,而sleep不用
      • wait方法执行后会释放对象锁,允许其他线程获得该对象锁
      • sleep如果在synchronized代码块中执行,并不会释放对象锁

    lock 和synchronized的区别

    • 语法层面:
      • synchronized是关键字,源码在jvm,是C++实现
      • lock是接口,由jdk提供,Java实现
      • 使用synchronized时,退出同步代码块时会自动释放锁,lock需要调用unlock手动释放锁
    • 功能层面
      • 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
      • lock提供了很多synchronized不具备的功能,例如获取等待状态、公平锁、可打断、可超速、多条件变量
      • lock有适合不同的场景使用,如ReentrantLock,ReentrantReadWriteLock(读多写少)
    • 性能层面
      • 在没有竞争或较小时,synchronized做了很多优化、如意向锁、轻量级锁,性能不赖
      • 在竞争激烈时,lock的实现通常能提供更好的性能

    volatile能否保证线程安全

    • 1.线程安全考虑三个方面:可见性、有序性、原子性
      • 可见性:一个线程对共享变量修改,另一个线程能看到最新的结果
      • 有序性:一个线程内代码按编写顺序执行
      • 原子性:一个线程内多行代码以一个整个执行,期间不能有其他线程的代码插队
    • 2.volatile能保证可见性、有序性,但不保证原子性,在多线程时可能会在执行了一半时切换线程,共享变量交错覆盖
    //共享变量 sum = 10
    //t1线程内容
    {
        int s = sum;
    	s -= 5;  //如果在这时线程切换就会结果覆盖了
    	sum = s;
        return sum;
    }
    
    //t2线程
    {
        int s = sum;
        s += 5;
        sum = s;
        return sum;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 可见性问题:是JIT优化更改了热点代码(常调用的方法,大量循环的代码),可以添加参数-Xint表示只需要翻译成机器码不用jit优化。

    乐观锁和悲观锁

    • 1.悲观锁的代表是synchronized和 Lock锁
      • 其核心思想是【线程只有占有了锁,才能去操作共享变量,每次只有一个线程占锁成功,获取锁失败的线程,都得停下来等待】
      • 线程从运行到阻塞、再从阻塞到唤醒,涉及线程上下文切换,如果频繁发生,影响性能
      • 实际上,线程在获取synchronized和Lock锁时,如果锁已被占用,都会做几次重试操作,减少阻塞的机会
    • 2.乐观锁的代表是AtomicInteger,使用cas(compareAndSet——对比并修改)(一般配合volatile使用)来保证原子性
      • 其核心思想是【无需加锁,每次只有一个线程能成功修改共享变量,其它失败的线程不需要停止,不断重试直至成功】
      • 由于线程一直运行,不需要阻塞,因此不涉及线程上下文切换
      • 它需要多核cpu支持,且线程数不应超过cpu核数

    Hashtable_vs_ConcurrentHashMap

    • Hashtable 与ConcurrentHashMap都是线程安全的Map集合

    • Hashtable并发度低,整个Hashtable对应一把锁,同一时刻,只能有一个线程操作它

    • 1.8之前ConcurrentHashMap使用了Segment+数组+链表的结构,每个Segment对应一把锁,如果多个线程访问不同的Segment,则不会冲突

    • 1.8开始ConcurrentHashMap将数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突

      • 1.7:ConcurrentHashMap底层数据结构是Segment+数组+链表

      • 1.8:ConcurrentHashMap底层数据结构是数组+(链表 | 红黑树)

      • 1.7:ConcurrentHashMap初始化直接创建了16大小的数组

      • 1.8:ConcurrentHashMap是在第一次使用put方法才创建

      • 1.7:ConcurrentHashMap扩容机制是超过3/4扩容

      • 1.8:ConcurrentHashMap扩容机制是满3/4就扩容

    • Hashtable初始数组大小是11、扩容阈值是超过3/4,扩容数量是原容量*2+1

    谈一谈对ThreadLocal的理解

    • ThreadLocal可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题
    • ThreadLocal同时实现了线程内的资源共享
    • 其原理是,每个线程内有一个ThreadLocalMap类型的成员变量,用来存储资源对象
      • 调用set方法,就是以ThreadLocal自己作为key,资源对象作为value,放入当前线程的ThreadLocalMap集合中
      • 调用get方法,就是以ThreadLocal自己作为key,到当前线程中查找关联的资源值
      • 调用remove方法,就是以ThreadLocal自己作为key,移除当前线程关联的资源值
  • 相关阅读:
    MongoDB聚合操作符:$addToSet
    Spring Boot中JUnit 4与JUnit 5的如何共存
    神经网络相关性系数r公式,神经网络预测数据
    什么样的台灯适合学生使用?五款暑假必入护眼大路灯分享
    ASP.NET Core Web API Action方法参数
    2022年高教社杯国赛赛题浅评
    保护您的 Web 应用程序的最佳开源 Web 应用程序防火墙
    全网好评!12个网络工程师必备工具!
    MySQL系列-安装配置使用说明(MAC版本)
    渔业安全生产综合管理指挥系统-航迹数据优化方案
  • 原文地址:https://blog.csdn.net/student_hwj/article/details/126897128