JAVA 并发八股

news2024/10/11 21:21:07

线程与进程区别

进程是正在运行的程序的实例,进程中包含了线程,每个线程执行不同的任务
不同进程使用不同的内存空间,在当前进程下所有线程可以共享内存空间
线程更轻量,线程上下文切换成本一般上要比进程上下文切换低(上下文切换指从一个线程切换到另一个线程)

并行和并发有什么区别

在多核CPU下,

  • 并发指同一时间,多个线程轮流使用一个或多个CPU
  • 并行是指在同一时间内,多个CPU同时执行多个不同的线程

线程创建方式

继承Thread类

public class MyThread extends Thread{
    @Override
    public void run(){
        System.out.println("MyThread...run...");
    }

    public static void main(String[] args){
        // 创建MyThread对象
        Mythread t1 = new MyThread();
        t1.start();
    }
}

实现runnable接口

public class MyRunnable implements Runnable{
@Override
public void run(){
	System.out.println("Runing");
}
public static void main(String[] args){
	MyRunnable mr = new MyRunnable();
	Thread t1 = new Thread(mr);
	Thread t2 = new Thread(mr);
	t1.start();
	t2.start();
}
}

实现Callable接口

在这里插入图片描述

线程池创建线程

在这里插入图片描述

Runnable和Callable区别

  1. Runnable接口的run方法没有返回值
  2. Callable接口call方法有返回值,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
  3. Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部笑话,不能继续抛出

在启动线程时,可以使用run()方法吗?run()方法与start()有什么区别?

  • start():用来启动线程,通过该线程调用run方法中定义的代码逻辑。start方法只能被调用一次
  • run():封装了要被线程执行的代码,可以被调用多次。

线程的状态之间如何变化

  1. 创建线程对象是新建状态
  2. 调用了start()方法转变为可执行状态
  3. 线程获取了CPU的执行权,执行结束是终止状态
  4. 可执行状态的过程中,如果没有获取CPU的执行权,可能回切换为其他状态
    • 如果没有获取锁(synchronized或lock)进入阻塞状态,获得锁再切换为可执行状态
    • 如果线程调用了wait()方法进入等待状态,其他线程调用notify()唤醒后可切换为可执行状态
    • 如果线程调用了sleep(50)方法,进入计时等待状态,到时间后可切换为可执行状态
      在这里插入图片描述

notify()和notifyAll()有什么区别

notifyAll:唤醒所有wait的线程
notify: 只随即唤醒一个wait线程

在java中wait和sleep方法的不同?

共同点

wait(), wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态

不同点

  • 方法归属不同: sleep(long) 是Thread的静态方法而wait(),wait(long)都是Object的成员方法,每个对象都有
  • 醒来时机不同: 执行sleep(long)和wait(long) 的线程都会在等待相应毫秒后醒来
    wait(long)和wait还可以被notify唤醒,wait()如果不唤醒就一直等下去
    它们都可以被打断唤醒
  • 锁特性不同
    • wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
    • wait方法执行后回释放对象锁,允许其他线程获得该对象锁;而sleep如果在synchronized代码块中执行,并不会释放对象锁

如何停止一个正在运行的线程

有三种方式可以停止线程

  1. 使用退出标志,使线程正常退出,即当run方法完成后线程终止
  2. 使用stop方法强行终止(不推荐)
  3. 使用interrupt方法中断线程。打断阻塞的线程(sleep,wait,join),线程会抛出InterruptedException异常;打断正常的线程,可以根据打断状态来标记是否退出线程。

synronized关键字的底层原理

  • Synchronized【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有对象锁
  • 它的底层由monitor实现,monitor是jvm级别的对象,线程获得锁需要使用对象关联monitor
  • monitor内部有三个属性,分别是owner、entrylist、waitset。其中owner是关联的获得锁的线程,且只能关联一个线程;entrylist关联的是处于阻塞状态的线程;waitset关联的是处于waiting状态的线程;

Monitor实现的锁属于重量级锁,你了解过锁升级吗?

Java的synchronized有偏向锁、轻量级锁、重量级锁三种形式,分别对应了锁只能被一个线程持有、不同线程交替持有和多线程竞争锁三种情况。一旦锁发生了竞争,都会升级为重量级锁。

锁的类型描述
重量级锁底层使用的Monitor实现,涉及用户态和内核态的切换、进程的上下文切换,成本较高,性能较低
轻量级锁线程加锁的时间是错开的,也就是没有竞争,可以使用轻量级锁来优化。轻量级修改了对象头的锁标志,相对重量级锁的性能提升了很多。每次修改都是CAS操作,保证原子性
偏向锁当一段很长的时间内都只被一个线程使用时,可用使用偏向锁。在第一次获得锁时,会有一个CAS操作,之后改线程在获得锁只需要判断mark word中是否是自己的线程id即可,而不是开销相对较大的CAS命令。

谈谈JMM(Java内存模型)

  • JMM,又称Java内存模型,定义了共享内存中多线程程序读写操作的行为规范,通过这些规则来规范对内存的读写操作从而保证指令的正确性
  • JMM把内存分为两块,一块是私有线程的工作区域(工作内存),一块是所有线程的共享区域(主内存)
  • 线程和线程之间是相互隔离的,线程和线程交互需要通过主内存

CAS是什么

  • 全称是Compare And Swap,体现的是一种乐观锁的思想,在无锁状态下保证线程操作共享数据的原子性
  • CAS使用到的地方很多:AQS框架、AtomicXXX类
  • 在操作系统共享变量的时候使用的自旋锁,效率上更高一点
  • 底层是调用的Unsafe类中的方法,都是操作系统提供的。

乐观锁和悲观锁的区别

  • 乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了,具体方法可以使用版本号机制或 CAS 算法。乐观锁通常多用于写比较少的情况(多读场景,竞争较少),这样可以避免频繁加锁影响性能。不过,乐观锁主要针对的对象是单个共享变量。
  • 悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。悲观锁通常多用于写比较多的情况(多写场景,竞争激烈),这样可以避免频繁失败和重试影响性能,悲观锁的开销是固定的。

对volatile关键字的理解

一但一个共享变量(类成员变量、静态成员变量)被volitile修饰,其具有了两层含义

  1. 保证线程间的可见性: 用volatile修饰共享变量,能够防止编译器等优化发生让一个变量的修改对另一个线程可见
  2. 禁止进行指令重排序:volatile修饰共享变量会在读写共享变量时加入不同的屏障,阻止其它读写操作越过屏障,从而达到阻止重新排序的效果。
  • 写操作加的屏障是阻止其上方任何写操作排到volatile变量之下
  • 读操作阻止下方其它读操作越过屏障拍到volatile变量读之上

synchronized 和 volatile 有什么区别?

  • volatile 关键字是线程同步的轻量级实现,所以 volatile性能肯定比synchronized关键字要好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

什么是AQS

  • AQS是多线程的队列同步器,在基础框架中使用的锁机制
  • AQS内部维护了一个先进先出的双向队列,队列中存储的是排队的进程
  • 在AQS内部还有一个属性state,这个state就相当于是一个资源,默认为0(无锁状态),如果队列中的有一个线程成功将state修改为1,则视为改线程获取了资源。
  • 对state修改的时候使用了cas操作,保证多个线程修改的情况下的原子性。

ReentrantLock的实现原理

ReentrantLock,可重入锁,相对于synchronized具备以下特点

  • 可中断
  • 可设置超时时间
  • 可以设置公平锁
  • 支持多个条件变量
  • 与sunchronized一样,支持重入

ReentranLock主要利用CAS+AQS队列来实现,支持公平锁和非公平锁,两者的实现类似

构造方法接收一个可选的公平参数(默认非公平锁),当设置为true时,表示公平锁,否则为非公平锁。

非公平锁

在这里插入图片描述

  • 线程抢锁后使用cas的方式修改state状态,修改状态成功为1,让exclusiveOwnerThread属性指向当前线程,获取锁成功
  • 如果修改失败,将进入双向队列等待
  • 当exclusiveOwnerThread属性为null时,会唤醒双向队列中等待的线程
  • 公平锁按先后顺序获取锁,非公平锁则不再排队中的线程也可以参与抢锁。

synchronized与Lock有什么区别

  • 语法层面
    synchronized是关键字,由c++实现

Lock是接口,由java语言实现

使用synchronized时,退出同步代码块锁会自动释放;而使用Lock时,需要调用unlock手动释放锁

  • 功能层面
    二者均属于悲观锁,都具备基本的互斥、同步、锁重入功能

Lock提供了许多synchronized不具备的功能,例如公平锁、可打断、可超时、多条件变量

Lock有适合不同场景的实现,例如ReentrantLock(可重入锁)

  • 性能层面
    在竞争较小时,synchronized做了大量优化,如偏向锁、轻量级锁等

在竞争激烈时,Lock通常性能更好

死锁产生的条件

当一个线程需要同时获得多把锁时,就容易发生死锁。假设存在两把锁LockA和LockB,线程t1和t2都需要这两把锁,但是t1获取了LockA,t2获取了LockB,这时就产生了死锁。

ConcurrentHashMap

线程安全的HashMap。

  1. 底层数据结构
  • JDK1.7底层采用分段的数组+链表实现
  • JDK1.8采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑树
  1. 加锁的方式
  • JDK1.7采用Segment分段锁,底层使用的是ReentrantLock
  • JDK1.8采用CAS添加新节点,采用synchronized锁定链表或红黑树的首节点,相对Segment分段锁粒度更细,性能更好

JDK1.7 ConcurrentHashMap

首先将数据分为一段一段(这个“段”就是 Segment)的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。

ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment继承了ReentrantLock,承担可重入锁的功能,HashEntry 用于存储键值对数据。

线程池

线程池的核心参数

public ThreadPoolExecutor(int coolPoolSize,
                    int maximumPoolSize,
                    long keepAliveTime,
                    BlockingQueue<Runnable> workQueue,
                    ThreadFactory threadFactory,
                    RejectedExecutionHandler handler)
  • corePoolSize 核心线程数目
  • maximumPoolSize 最大线程数目=(核心线程数目+临时线程数目)
  • keepAliveTime 生存时间(指临时线程的生存时间,生存时间内没有新任务,此线程资源会释放)
  • unit 时间单位 (临时线程的生存时间单位,如秒、毫秒等)
  • workQueue 当没有空闲核心线程时,新任务会加入到此排队队列,队列满会创建救急线程执行任务
  • threadFactory 线程工厂(可以定制线程对象的创建,如设置线程名字、是否为守护线程)
  • handler 拒绝策略(当所有线程在忙,workQueue也满时,会按照策略拒绝新线程)

线程池的执行原理

在这里插入图片描述

线程池中有哪些常见的阻塞队列

线程池中常见的阻塞队列有ArrayBlockQueue和LinkedBlockingQueue

LinkedBlockingQueueArrayBlockQueue
默认无界,支持有界有界
底层是链表底层是数组
懒惰的,创建节点时添加数据提前初始化Node数组
入队会产生新的NodeNode需要提前创建好
两把锁一把锁

如何确定核心线程数

  1. 对于高并发、任务执行时间短的任务 -->(CPU核数+1),减少线程上下文切换
  2. 并发不高、任务执行时间长
  • IO密集型的任务 -->(CPU核数*2+1)
  • 计算密集型的任务 --> (CPU核数+1)
  1. 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体框架的设计,首先看这些业务中的数据能否进行缓存,其次增加服务器

线程池的种类

常见的有四种

  1. 创建使用固定线程数的线程池 FixedThreadPoolExecutor
    • 核心线程数与最大线程数一样,没有救急线程
    • 阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE
      适用于任务量
  2. 单线程化的线程池 SingleThreadPoolExecutor
    只会用唯一的工作线程执行任务,保证所有任务按照指定顺序(FIFO)执行 SingleThreadPoolExecutor
    • 核心线程数和最大线程数都是1
    • 阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE
      适用于按照顺序执行的任务要求
  3. 可缓存线程池 CatchedThreadPoolExecutor
    创建一个可缓存的线程池,如果线程池长度超过处理需求,可回收空闲线程,若无可回收,则新建线程
    • 核心线程数为0
    • 最大线程数是Integer.MAX_VALUE
    • 组赛队列为SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等待一个移出操作
      适用于任务数比较密集,但每个任务执行时间较短的情况
  4. 提供了延迟和周期执行功能的线程池 ScheduledThreadPoolExecutor
    在这里插入图片描述

为什么不建议用Executors创建线程池

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这种处理方式更加明确线程池的运行规则,避免资源耗尽风险

  1. FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
  2. CachedThreadPool: 允许的船舰线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

使用场景

CountDownLatch

CountDownLatch(闭锁/倒计时锁)用来进行线程同步协作,等待所有线程完成倒计时(一个或多个线程,等待其它多个线程完成某件事后才能执行)

  • 构造参数用来初始化等待计数值
  • await() 用来等待计数清零
  • countDown() 用来让计数减一
    在这里插入图片描述

异步线程 ⭐️

为了避免下一级方法影响上一级方法(性能考虑),可以使用异步线程调用下一个方法(不需要下一级方法返回值),可以提升方法响应时间。

如何控制某个方法允许并发访问线程的数量

可以通过信号量(semaphore)进行限制,通常用于有明确访问数量限制的场景。

使用步骤

  1. 创建Semophore对象,可以指定容量
  2. semaphore.acquire():请求一个信号量,这时候的信号量个数-1(一旦信号量个数变为附属,再次请求的时候就会阻塞,直到其他线程释放信号量)
  3. semaphore.release():释放一个信号量,此时信号量个数+1

谈谈你对ThreadLocal的了解

ThreadLocal是多线程中解决线程安全的一个操作类,会为每个线程都分配一个独立的线程副本,从而解决了变量并发访问冲突的问题。ThreadLocal同时实现了线程的资源共享。

ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。

** 基本使用方法**

  • set() 设置值
  • get() 获取值
  • remove() 清除值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vueJS中wowjs、animate、swiper的使用

原文关注公众号 本文演示利用swiper纵向全屏滚动 npm 安装 wow.js&#xff0c;安装 wow.js后animate.css会自动安装&#xff1b; npm install wowjs --save-dev npm 安装 animate.css animate.css文档&#xff1a;http://5kzx.cn/doc.html npm install animate.css --save …

Python和MATLAB及C++和Fortran胶体粒子数学材料学显微镜学微观流变学及光学计算

&#x1f3af;要点 二维成像拥挤胶体粒子检测算法粒子的局部结构和动力学分析椭圆粒子成链动态过程定量分析算法小颗粒的光散射和吸收活跃物质模拟群体行为提取粒子轨迹粘弹性&#xff0c;计算剪切模量计算悬浮液球形粒子多体流体动力学概率规划全息图跟踪和表征粒子位置、大小…

创建docker虚拟镜像,创建启动服务脚本

进入系统命令服务目录 编辑服务 [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service Wantsnetwork-online.target [Service] Typenotify ExecStart/usr/bin/dockerd ExecReload/bin/…

Gradle 插件获取所有依赖项,类似 androidDependencies?

诉求 在打包过程中我想知道某个模块的信息&#xff0c;比如&#xff1a; 模块androidx.work:work-runtime是否被依赖&#xff1f;模块androidx.work:work-runtime的版本号是多少&#xff1f; 我们利用 Android studio 已有的任务androidDependencies&#xff0c;双击执行很容…

PyQt5写好的py文件生成可执行的exe文件【Nuitka】

文章目录 1.Nuitka引入2.Nuitka与Pyinstaller对比Nuitka安装 3.Nuitka指令4.参数以及作用5.多文件格式封装完成后可删除文件6.运行问题问题1问题2 1.Nuitka引入 看过我上一篇PyQt5写好的py文件生成可执行的exe文件【Pyinstaller】的应该了解到用PyQt5写的界面程序可以通过Pyins…

安卓冻屏bug案例作业分享-千里马学员wms+input实战作业

背景&#xff1a; 近期有学员反馈在aosp14高版本上有了一个新窗口TaskBar&#xff0c;这个但是有需求就是对这个TaskBar进行隐藏&#xff0c;所以有一个需要对这个TaskBar进行进行隐藏需求 隐藏TaskBar需求做了之后发现有如下bug&#xff1a; 问题复现步骤&#xff1a; 因…

新款示波器RTE1104罗德与施瓦茨RS RTE1102原装二手

罗德与施瓦茨R&S RTE1104触摸屏RTE1102新款示波器 R&S-RTE1000 示波器系列&#xff1a; RTE1022标配&#xff1a;200MHz带宽&#xff0c;2通道&#xff0c;5GSa/s采样率&#xff0c;200M存储深度&#xff0c;16个数字通道&#xff08;选配&#xff09; RTE1032标配&a…

HCIP--以太网交换安全(二)端口安全

端口安全 一、端口安全概述 1.1、端口安全概述&#xff1a;端口安全是一种网络设备防护措施&#xff0c;通过将接口学习的MAC地址设为安全地址防止非法用户通信。 1.2、端口安全原理&#xff1a; 类型 定义 特点 安全动态MAC地址 使能端口而未是能Stichy MAC功能是转换的…

解决PyCharm 2023 Python Packages列表为空

原因是因为没有设置镜像源 展开 > 之后&#xff0c;这里 点击齿轮 添加一个阿里云的源 最后还需要点击刷新 可以选择下面的任意一个国内镜像源&#xff1a; 清华&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 阿里云&#xff1a;http://mirrors.aliyun.com/…

asp.net core Partial 分部视图、视图组件(core mvc 才支持)、视图、Razor组件 、razor pages

分部视图 》》》传参 》》两个东西换个名称&#xff0c;PartialView()>渲染视图>不带Layout 部分视图与普通视图没太大区别&#xff0c;它可以将重复使用的HTML内容结合起来&#xff0c;可以单独使用。 一般命名是在名称前面加下划线&#xff0c;放在/Views/Shared 目…

【cocos creator】输入框滑动条联动小组建

滑动条滑动输入框内容会改变 输入框输入&#xff0c;滑动条位置改变 const { ccclass, property } cc._decorator;ccclass() export default class SliderEnter extends cc.Component {property({ type: cc.Float, displayName: "最大值", tooltip: "" }…

基于Web的停车场管理系统(论文+源码)_kaic

摘要 我国经济的发展愈发迅速&#xff0c;车辆也随之增加的难以想象&#xff0c;因此车位的治理也越来越繁杂&#xff0c;为了方便停车位相关信息的管理&#xff0c;设计开发一个合理的停车位管理系统尤为重要。因而&#xff0c;具有信息方便读取和操作简便的停车位管理系统的设…

Java基础-知识点

文章目录 数据类型包装类型缓存池 String概述不可变的含义不可变的好处String、StringBuffer、StringBuilderString.intern() 运算参数传递float与double隐式类型转换switch 继承访问权限抽象类与接口super重写与重载**1. 重写(Override)****2. 重载(Overload)** Object类的通用…

H3C GRE over IPsec VPN 实验

H3C GRE over IPsec VPN 实验 实验拓扑 ​​ 实验需求 某企业北京总部、上海分支、武汉分支分别通过 R1,R3,R4 接入互联网,配置默认路由连通公网按照图示配置 IP 地址,R1,R3,R4 分别配置 Loopback0 口匹配感兴趣流,Loopback1 口模拟业务网段北京总部拥有固定公网地址…

VMware Fusion 13.6.1 发布下载,修复 4 个已知问题

VMware Fusion 13.6.1 发布下载&#xff0c;修复 4 个已知问题 VMware Fusion 13.6.1 for Mac - 领先的免费桌面虚拟化软件 适用于基于 Intel 处理器和搭载 Apple 芯片的 Mac 的桌面虚拟化软件 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-fusion-13/ 查看最新…

找不到xinput1_3.dll怎么解决,快来试试这个几个方法

在计算机系统运行过程中&#xff0c;当我们遭遇“找不到xinput1_3.dll”这一错误提示时&#xff0c;实际上正面临一个软件兼容性、系统组件缺失以及游戏或应用程序无法正常启动的关键问题。深入探究这一现象&#xff0c;我们会发现它可能引发一系列连带问题&#xff0c;例如某些…

【课程设计/毕业设计】Java家政预约管理系统源码+开发文档

项目介绍 一直想做一款家政管理系统&#xff0c;看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。学习过程中遇到问题可以咨询留言。 在线体验 http://jiazheng.gitapp.cn/ 源码地址 https://github.com/geeeeeeeek/java_jiazh…

JVM和GC案例详解

接上文JVM环境配置说明&#xff1a;上文博客 一、JVM远程连接设置 1. JMX方式连接(这种方式没有GC监控)&#xff0c;设置如下 2. 连接成功后可以查看基础配置参数(和服务器配置一致) 2. jstatd方式连接(这种方式没有CPU监控) 添加jstatd方式连接 双击Tomcat&#xff0…

python可变数据类型和不可变数据类型

先看一段代码。 value1 10 value2 value1 print(value1) print(value2) value1 30 print(value1) print(value2)再看另一段代码。 list1 [1,2,3,4] list2 list1 print(list1) print(list2) list1.append(5) print(list1) print(list2)第一段代码中&#xff0c;value2的值…

深入解析:如何使用LangChain进行RAG处理半结构化数据

深入解析&#xff1a;如何使用LangChain进行RAG处理半结构化数据 引言 在处理半结构化数据如PDF文件时&#xff0c;如何有效提取信息是一个挑战。本文将介绍如何使用LangChain的RAG处理模板处理这样的数据。我们将探讨安装、使用和在项目中集成的完整过程。 主要内容 环境设…