【百面成神】多线程基础16问,你能坚持到第几问

简介: 【百面成神】多线程基础16问,你能坚持到第几问

1.线程池

创建线程需要时间和资源,如果当需要使用一个线程时再创建一个线程,响应时间会变长。可以在程序启动时就创建若干线程来以备响应之需,这些线程由线程池来统一管理。

有以下参数:

核心线程数 corePoolSize

最大线程数 maxPoolSize

存活时间Keepalivetime

单位Unit

工作队列 workqueue

线程工厂 threadFactory

拒绝策略Handler


2.创建线程有哪些方法

继承Thread类

实现Runnable接口(如果要继承其它类,可以实现Runnable接口)

实现Callable接口(和FutureTask结合,有返回值、可以抛出异常)

使用线程池


3.什么是ABA问题

通过多次读取一个值,根据值是否有变化判断数据是否发生改变,但是可能出现值回退的情况。


4.什么是CAS

在引入CAS以前,java通过sychronized加锁,以避免临界资源竞争的问题。但是这是一种悲观锁,获取不到锁的线程会被挂起,上下文切换等也会造成时间损耗,还可能出现优先级较高的线程等待优先级较低的线程释放锁资源的问题。而CAS是一种乐观锁机制,他的全程是CompareAndSet,他他不会立刻加锁,CAS有三个值,内存值,预期值,新值,如果内存值=预期值,就说明临界资源没有被其它线程修改,可以将其更新为新值。


CAS能够从底层硬件级别对于cpu效率进行提升,因此其效率也很高。


但是,CAS可能有ABA问题

5.说一说线程的生命周期

线程刚刚创建时是NEW,开始运行会变成Runnable,如果被IO阻塞或者同步锁阻塞会变成Blocked,如果永久等待状态是Waiting,如果是等待被唤醒是Timed_Waiting,执行完成是Terminated


6.JMM模型

线程之间共享的变量存放在主存中,而每个线程的私有变量存放在各自的本地内存中。


7.线程死锁问题&线程池满的问题

线程A持有独占锁a,并且尝试获取独占锁b,线程B持有独占锁b,并且尝试获取独占锁a。


在工作中会先切换到问题出现的代码版本分支,使用jstack命令做线程dump,并且使用threadIO排查线程死锁问题。曾经遇到一个场景,就是将Lock改为了tryLock,但是没有设置时间参数。最后设置了时间参数解决了问题。


想要避免死锁,可以通过按顺序访问资源来实现。比如线程A,B都是先访问a,再访问b,释放锁的顺序与获取锁的顺序相反。就可以避免死锁。

如果实际可行,也可以一次性获取所有资源。

占用锁资源的线程再去申请锁资源时,如果申请不到锁资源,先释放它现有的锁资源。

还需要注意锁的粒度尽量设置的细点,尽量使用JUC提供并发类,而不要使用手写锁。

8.锁升级的原理

记录threadid,获得偏向锁;再次访问比较线程id,获得轻量级锁,进行一定次数的锁自旋,获得重量级锁。


9.ThreadLocal

每个线程都有一个自己的变量副本,彼此之间隔离,互相不干扰。使用ThreadLocal可以提供比使用sychorinized更简单的一种线程安全机制。在session管理,数据库连接等场景中会有应用。


10.volatile关键字的作用

可以保证修改的可见性,同时避免指令重排序。


11.synchornized和Lock的区别

作用范围:使用syn可以给代码块,方法和变量加锁,而使用lock只可以给代码块加锁

使用方法:使用lock需要手动释放锁

API:使用Lock可以知道是否成功获取锁,但是使用sync却无法做到

实现原理:

Lock的加锁和解锁过程是由java代码配合native方法实现的。

而synchornized是由JVM来直接管理其加锁和解锁的过程。

Synchronized的实现原理是:

先判断markword(对象头中的一个记录字段),是否为可偏向状态,如果是则获取偏向锁(这其实是为了提高单线程下的执行效率,可以理解成为没有锁,直接进入到同步代码块,并在markword中记录下这个线程id),如果由其它线程来抢占锁资源,就会根据当前状态(是否通过CAS算法竞争到锁资源)判断是否要进行锁膨胀,膨胀为轻量级锁。

轻量级锁使用CAS无锁算法。会先检查锁资源对象头的Mark word,看看当前的锁对象是否为无锁状态,如果没有就会先从栈帧中申请一个LockRecord空间。将Markword中的数据进行备份。然后通过CAS算法更新markword,将其指向LockRecord。如果更新成功,则进入同步代码块。如果没有更新成功,检查是否当前线程之前已经获取了锁还没有释放。如果已经获取也可以进入代码块,否则会进行多次锁自旋,需要膨胀为重量级锁。

在java内部每一个对象或类都有一个monitor监视器。重量级锁会通过monitor直接向操作系统申请互斥量

虚拟机还会自动检查,如果符合条件会进行锁消除(无竞争可能)和锁粗化(循环中使用锁,会自动扩大加锁范围)。

Lock的实现原理:

Lock其实是一个接口,它有ReentrantLock等实现类。

以ReentrantLock为例。它继承了AQS,其底层最后都是调用的AQS的方法来实现的。本质上就是一个双向链表通过不断的进行CAS自旋操作来获取锁。这也是它性能好的原因:避免了线程进入内核态的阻塞状态。


12.并发和并行的区别

并发是为了cpu交替执行不同任务的能力,主要是为了避免cpu资源浪费在等待IO情形,提高cpu的利用率。

并行是在多核环境下,不同cpu执行不同任务的能力,不同核心之间互不干扰,是真正的同时执行。

常见的并发场景有:秒杀活动、股票交易系统等

高并发场景的瓶颈在于:

服务器带宽资源不够、web线程连接数不够、数据库连接上不去。

对应解决并发问题的思路有:

增加网络带宽、DNS域名解析器解析分发到多台服务器

负载均衡:如使用nginx

数据库查询优化,读写分离,分表,合理使用索引等

高并发场景下需要考虑的另一个问题是线程安全问题:

所谓线程安全就是指多线程环境下,同一代码每次执行的结果,都与单线程环境执行的结果一致,而且其它变量的结果,也与预期一致。


13.线程和进程的区别

进程就是一个独立的程序,具有独立的内存空间,比如桌面点开一个IE浏览器网页就是一个进程,点开一个QQ就是另外一个进程。

线程是进程中执行运算的最小单位,一个进程中有多个线程。线程之间既有本地内存,又有共享内存。

进程之间的通信方式有:

管道(半双工、单向流动)、信号量(是一个计数器、常常作为锁机制用来控制多个进程或者线程对共享资源的访问)、消息队列等

线程之间的通信方式有:

锁机制(互斥锁、读写锁等)

补充:可以接着说多线程引入的原因(即并发)

14.start()和run()有什么区别?

Start()内部调用了run()方法。

如果直接调用一个run()方法,不会创建一个新的线程,而是有原来的线程执行该方法。而使用start()方法一定会创建一个新的线程。

调用了start()线程是进入就绪状态,只有获取到了cpu资源才会进入开始状态,才会执行里面调用的run()方法,而不是立即执行。


15.如何停止一个线程?

Java没有提高api停止一个线程,在jdk1.0版本中,提供了suspend,resume,stop等api,但是由于潜在的死锁风险,后续版本中被弃用了。如果想要手工停止一个线程,可以设置一个使用vilotile修饰的布尔变量,在run方法中判断布尔变量的值,来决定是否继续执行run方法。

也可以是用interrupt来实现(推荐:分为有sleep和wait等相应中断的情况[try-catch异常即可]和不含上述方法[while循环增加判断条件即可]的情况)。

参考博客:Java如何停止一个线程_风在哪的博客-CSDN博客

16.notify和notifyAll有什么区别?

当一个线程调用了wait方法以后,它会进入等待池,等待池中的线程不会参与锁资源的竞争。

当调用notify方法时,会随机唤醒一个锁池中线程进入锁池。

当调用notifyAll方法时,则会唤醒所有等待池中的线程,使他们都进入到锁池。

进入到锁池的线程会竞争锁资源,优先级高的线程竞争到锁的可能性较大。

竞争到锁资源以后,线程就会执行synchorinized代码块,执行结束后释放锁,此时其它锁池中的对象会继续下一次竞争。


相关文章
|
8月前
|
存储 算法 数据库
十天学完基础数据结构-第一天(绪论)
十天学完基础数据结构-第一天(绪论)
41 0
|
8月前
|
NoSQL Java 数据库连接
12W字大厂横跳神技!阿里P8熬夜三个月肝出Java面试必备真题手册
招聘市场开始回暖了?! 很多大厂和央企国企已经陆续开放 hc!是时候开始好好准备一下挑战高薪、冲击大厂了! 从事 Java 开发的小伙伴都会发现,面试一定逃不过 Spring 的关卡。 在 Spring 源代码能力相对较弱的情况下参加面试,很容易被面试官问住,只能和心仪的 offer 失之交臂~
|
8月前
|
消息中间件 Java 关系型数据库
膜拜!阿里自爆十万字Java面试手抄本,脉脉一周狂转50w/次
新冠疫情已经是第三个年头了,虽然国内防控做得非常好,但是他对职场的影响还在,一个月后即将又迎来一次大考。近两年企业越来越不好做,导致面试时对程序员的要求越来越高,越来越挑剔;
|
10月前
|
域名解析 网络协议 大数据
时间有界 梦想无疆(NEBASE第十三课)(一)
时间有界 梦想无疆(NEBASE第十三课)(一)
72 0
时间有界 梦想无疆(NEBASE第十三课)(一)
|
10月前
|
网络协议
时间有界 梦想无疆(NEBASE第十三课)(二)
时间有界 梦想无疆(NEBASE第十三课)(二)
58 0
|
12月前
|
存储 缓存 算法
【百面成神】JVM基础12问,你能坚持到第几问
【百面成神】JVM基础12问,你能坚持到第几问
|
存储 机器学习/深度学习 算法
【第十四届蓝桥杯考前速成】必考知识点及代码模板总结,看完至少多拿50分
四、简单图论 1、单源最短路径 2、多源最短路 3、最小生成树 五、动态规划 1、0-1背包 2、完全背包 3、多重背包 4、线性DP 总结
109 0
|
存储 算法 Linux
宿友用一把王者的时间入门了【二叉树】,你又懂多少呢?乌拉~~
宿友用一把王者的时间入门了【二叉树】,你又懂多少呢?乌拉~~
114 0
宿友用一把王者的时间入门了【二叉树】,你又懂多少呢?乌拉~~
|
存储 消息中间件 缓存
四万字爆肝总结java多线程所有知识点(史上最全总结)
全文从多线程的实现方式、线程的状态、线程的方法、线程的同步、线程的通讯、等角度对多线程的基础知识进行总结
387 1
四万字爆肝总结java多线程所有知识点(史上最全总结)
|
存储 安全 Java
集合很简单?开什么玩笑?肝了一周,全是精华,万字讲解,面试再不怕集合问题了
ArrayList 是容量可变的⾮线程安全列表,使⽤数组实现,集合扩容时会创建更⼤的数组,把原有数组复制到新数组。⽀持对元素的快速随机访问,但插⼊与删除速度很慢。ArrayList 实现了 RandomAcess 标记接⼝,如果⼀个类实现了该接⼝,那么表示使⽤索引遍历⽐迭代器更快。
93 0
集合很简单?开什么玩笑?肝了一周,全是精华,万字讲解,面试再不怕集合问题了