当前位置:   article > 正文

【Java面试篇】并发编程_java并发编程面试

java并发编程面试

java集合由两个接口实现,Collection接口用于存放单一元素,Map接口用于存放键值对。

1.Hashtable & HashMap & ConcurrentHashMap

HashMap:

  • JDK1.7之前:底层采用数组+链表的方式实现,链表是为了解决hash冲突。当多线程运行环境下扩容容易导致链表死循环,因为采用了头插法。
    参考博客
  • JDK1.8及之后:底层采用了数组+链表+红黑树的方式实现,采用尾插法,解决了链表死循环的问题。当数组长度不到64时,会先对数组进行扩容。之后如果链表的大小超过8,就很转换为红黑树。
  • 线程不安全

Hashtable:
在这里插入图片描述

底层采用了数组+链表,但是内部方法使用了synchronized关键字修饰,是线程安全的。但是由于一个线程访问的时候其他线程无法访问,效率比HashMap低。

ConcurrentHashMap:

JDK1.7之前:使用数组+链表实现,线程安全,使用Segment分段锁的方式,将数组分成一段一段的,当a段加锁时b段还能被其他线程访问。
在这里插入图片描述
JDK1.8及之后:数组+链表+红黑树,丢弃分段锁,使用CAS+Sychronized实现并发安全性。锁的粒度更小。
在这里插入图片描述
参考链接

2.进程与线程
进程是程序执行的一次过程,是动态的。而线程是比进程更小的执行单位,是cpu运行的基本单位。

  • 存储:线程共享同一进程的堆(存放新建的对象)和方法区(存放已被加载的类信息、常量、静态变量等)。
    但是每个线程有自己的程序计数器(指向要执行的指令地址)以及虚拟机栈和本地方法栈(存放局部变量)。
  • 线程死锁

条件:
1.请求并保持条件:一个线程因获取资源而阻塞时,对已获取的资源保持不放。
2.不可剥夺条件:线程自己获得的资源在没使用完自己其他线程不可强行剥夺,只有自己使用完才释放资源
3.互斥条件:该资源任何时刻只能由一个线程占用。
4.循环等待条件:若干线程形成一种头尾相接的环形等待资源关系。

预防死锁:
1.破坏请求并保持:一次性申请所有资源
2.破坏不可剥夺条件: 一个线程获取部分资源后继续获取其他资源,如果申请不到,可以释放它所占用的资源。

  • 线程生命周期和状态:

1.NEW: 初始状态,线程被创建出来但没有被调用 start() 。
2.RUNNABLE: 运行状态,线程被调用了 start()等待运行的状态。
3.BLOCKED:阻塞状态,需要等待锁释放。4.WAITING:等待状态,表示该线程需要等待其他线程做出一些特定动作(通知或中断)。5.TIME_WAITING:超时等待状态,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待。
6.TERMINATED:终止状态,表示该线程已经运行完毕

3.可以直接调用Thread类的run方法吗?
最好不要,因为只有调用start()方法才会启动一个线程然后等时间片到的时候运行run()方法的内容。而直接调用run方法会把它当成是main程序下的一个普通方法执行。

4.volatile关键字
保证变量的可见性、禁止指令重排(插入内存屏障)。但是无法保证变量的原子性。使用volatile关键字的变量,线程读取的时候会从共享内存中读取,改写关键字后会同步刷新到共享内存。

5.乐观锁与悲观锁

  • 乐观锁假设每次访问共享资源的时候都不会出现问题,而悲观锁则相反
  • 乐观锁的实现有版本控制和cas算法。
    1.版本控制在每次修改之前都会读取版本号,当线程对资源操作完后如果版本号和之前一致就让版本号加一,同时同步数据。
    2.CAS则是将一个预期值与要更新的变量值进行比较,两者相等时就进行修改。只对单个共享变量有效。

存在的问题:
1.ABA问题:在A被修改为B之后可能修改为A,但是线程可能认为没有被修改。解决办法:加上时间戳或者版本号。
2.循环时间开销大:CAS会使用自旋操作进行不断重试。

6.synchronized

  • 使用:
    1.同步代码块:将所需要同步的代码块包裹起来
    2.同步实例方法:在方法声明的时候使用该关键字,调用该方法会自动获取该方法所属对象的锁。
    3.同步静态方法:调用该方法的时候会自动获取该方法所属类的锁。
  • 原理:
    在修饰同步代码块的时候,monitorenter指向开始位置,monitorexit指向结束位置。
    在修饰方法的时候,jvm采用ACC_SYNCHRONIZED标记符实现同步。不过两者的本质都是对对象监视器 monitor 的获取。

monitorenter,在判断拥有同步标识ACC_SYNCHRONIZED时会抢先进入线程,拥有Minitor的owner,计数器+1,执行完后计数器-1归0,被其他线程获得。

7.ReentrantLock和Synchronized的区别

  • 可重入性:两者皆可
  • 获取锁的方式:ReetrantLock需要显示获取和释放锁,而Synchronized则是隐式完成,在进入代码块的时候自动获取锁,在退出代码块的时候自动释放锁。
  • 锁的公平性:ReentrantLock可以作为公平或非公平锁使用。而synchronized是非公平锁
  • 可中断性:synchronized在获取锁的时候不可以中断,而ReentrantLock提供了可中断的锁的获取方式。

8.ThreadLocal
原理:每个线程都有一个ThreadLocalMap用来存储线程局部变量。当使用set方法时,会先获取线程的ThreadLocalMap对象,然后依据ThreadLocal计算出键值对存放的索引位置。
内存泄漏:ThreadLocal作为key是弱引用,而value是强引用。当线程不引用ThreadLocal时,ThreadLocal被回收,key为null,而value永远无法被GC回收,就会存在内存泄漏问题。

9.AQS
是一个抽象类,主要用来构建锁和同步器。如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁 实现的,即将暂时获取不到锁的线程加入到队列中。
CLH队列是双向队列,每个节点代表一个线程,其中有一个volatile关键字state(资源)表示同步状态,通过CAS更改。
--------------------------待更新-----------------------------------------------------

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/808848
推荐阅读
相关标签
  

闽ICP备14008679号