代码编织梦想

在这里插入图片描述

前 言
🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端
☕专栏简介:纯手打总结面试题,自用备用
🌰 文章简介:多线程最基础、重要的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代码块,执行结束后释放锁,此时其它锁池中的对象会继续下一次竞争。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_41708993/article/details/129705735

多线程技术_lwx_110的博客-爱代码爱编程

转 多线程技术 2016年11月07日 15:29:41 思维逆逝 阅读数:1884

Java 多线程学习(8)Object.wait()、Object.notify() 和 Object.notifyAll()-爱代码爱编程

转载请注明原创出处,谢谢! HappyFeet的博客 wait、notify 和 notifyAll 是 Object 对象所提供的几个方法,想必大家都见过,因为 Java 中的每个对象都有;不过在平时的工作中基本上不会用到,我是没遇到过。 这次想要深入的去学习这几个方法也是因为阅读 AQS 源码的缘故。 AQS 中实现了 Condit

【好文推荐】黑莓OS手册是如何详细阐述底层的进程和线程模型的?-爱代码爱编程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCvaP7LN-1596187339986)(https://imgkr2.cn-bj.ufileos.com/db1944cc-0cc4-4ee0-befd-c3cd2059a58d.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-b

分布式缓存灵魂十连,你能坚持几个?-爱代码爱编程

点击上方蓝色“方志朋”,选择“设为星标” 回复“666”获取独家整理的学习资料! 目录 前言 目前工作中用到的分布式缓存技术有redis和memcached两种,缓存的目的是为了在高并发系统中有效降低DB的压力,但是在使用的时候可能会因为缓存结构设计不当造成一些问题,这里会把可能遇到的坑整理出来,方便日后查找。 一. 常用的两种缓存技术

真是经典中的经典!MySQL+多线程+Redis+算法+网络-爱代码爱编程

前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚。本文就试着尽自己所能,对此做出一个较详细的解读。另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而初始化是指

最全java成神学习路线总结!!!_等风来.长的博客-爱代码爱编程

1确定自己是否要学习java 1.1其他语言对比 1).net、py、javascript、c/c++ 。 1.2学习的目的 1)为了当程序员?、高级测试?、黑客?、大数据?、???、主要目标、次要目标。 1.3成本分析 1.3.1能投入多少钱 1)几十块: 百度自学+购买少量书籍 效果1 2)几百块: 购买书或者电

刨根问底kafka 40问,看看你能坚持到第几问-爱代码爱编程

Kafka最初是由Linkedin公司开发的,是一个分布式的、可扩展的、容错的、支持分区的(Partition)、多副本的(replica)、基于Zookeeper框架的发布-订阅消息系统,Kafka适合离线和在线消息消费。它是分布式应用系统中的重要组件之一,也被广泛应用于大数据处理。Kafka是用Scala语言开发,它的Java版本称为Jafka。Lin

嵌入式一战成神-爱代码爱编程

嵌入式系统与应用开发 参考资料 《嵌入式系统设计与开发 第二版 中国矿业大学出版社》 成神导航 嵌入式系统与应用开发前言一、学习路线1、学习路线2、书籍推荐 二、《嵌入式系统设计与开发》学习1