并发编程篇

并发编程篇

image-20241127141258667

线程基础

image-20241127141427163

线程和进程的区别?

image-20241127141621961

image-20241127141726540

image-20241127141914660

面试总结

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

并行和并发的区别

image-20241127142103540

image-20241127142148649

面试总结

并行和并发的区别?
在多核CPU下:
	并发是同一时间应对多件事情的能力,多个线程轮流使用一个或多个CPU
	并行是同一时间动手做多件事情的能力,4CPU同时执行4个线程

创建线程的方式有哪些

image-20241127142503615

继承Thread类

image-20241127142608363

实现runnable接口

image-20241127142726336

实现Callable接口

image-20241127142937180

线程池创建线程

image-20241127143026454

面试总结

Q1:创建线程的方式有哪些?
1、继承Thread类
	编写一个类继承Thread,main函数里面新建一个Thread对象t1,然后t1.start()启动线程。

2、实现Runnable接口
	编写一个类实现Runnable,改写run方法,在主函数中,创建一个类实例,再创建一个线程,并将刚才的类实例交给线程,启动线程。

3、实现Callable接口
	编写一个类实现Callable接口,改写call方法,在主函数中,new 一个MyCallable 和 FutureTask,在新建一个线程管理FutureTask,启动线程。调用FutureTask的get方法可以获取执行结果。

4、线程池创建线程
	创建线程池对象,并调用其submit方法将新建的类实例放进去。最后关闭线程池。
	
	
Q2:Runnable 和 Callable有什么区别?
1、Runnable接口run方法没有返回值
2、Callable接口call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
3、Callable接口call方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛


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

线程包括哪些状态,状态之间如何变化?

image-20241127143705507

image-20241127144136438

面试总结

Q1:线程包括哪些状态?
新建New、可运行Runnable(包括就绪和运行两种状态)、阻塞Blocked、等待Waiting、时间等待Timed_Waiting、终止Terminated


Q2:线程状态之间如何变化?
1、创建线程对象是新建状态
2、调用了start()方法转变为可执行状态
3、线程获取到CPU的执行权,执行结束是终止状态
4、可执行状态过程中,如果没有获取到CPU执行权,会切换到其他状态
	4.1、如果没有获取锁,进入阻塞状态,获得锁就切换到可执行状态
	4.2、如果线程调用了wait()方法进入等待状态,其他线程调用notify()唤醒后可切换为可执行状态
	4.3、如果线程调用了sleep(50)方法,进入计时等待状态,到时间后可切换为可执行状态
	
	
	
创建线程(新建) -> 调用start方法(可执行) -> 执行完成(终止)
				此状态包含下面两种情况:
				获取到CPU(运行)
				未获取到CPU(就绪)
				
就绪态 			(拿到CPU执行权)--->  			运行态
				<----(其他线程抢走CPU)
				
就绪态    	   获得锁<--阻塞态<--无法获得锁			运行态
			notify()<--等待  <--wait()
			 到时间了<--计时等待<--sleep(50)

新建T1、T2、T3三个线程,如何保证它们按顺序执行?

image-20241127144610838

面试总结

新建T1T2T3三个线程,如何保证它们按顺序执行?
	可以使用线程中的join()方法解决:
	join():等待线程运行结束

在线程2 t2中调用线程1 t1.join(),它会阻塞调用此方法的线程进入timed_waiting,直到线程t1执行完成后,此线程t2再继续执行。

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

面试总结

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

java中wait和sleep方法不同?

面试总结

共同点:
	wait()、wait(long)和sleep(long)的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态。
	
不同点:
1、方法归属不同
	sleep(long)是Thread的静态方法
	wait()、wait(long)都是Object的成员方法,每个对象都有。
2、醒来时机不同
	执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来
	wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒,就一直等下去
	它们都可以被打断唤醒
3、锁特性不同(重点)
	wait方法的调用必须先获取wait对象的锁,sleep则无此限制(当线程 A 调用wait方法后,它会释放持有的对象锁(obj的锁),允许其他线程获取该锁并执行相应的操作。)
	wait方法执行后会释放对象锁,允许其他线程获得该对象锁(我放弃CPU,但你们还可以用)
	而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃CPU,你们也用不了)

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

面试总结

有三种方式可以停止线程:
1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2、使用stop方法强行终止(不推荐,方法已作废)
3、使用interrupt方法中断线程
	3.1、打断阻塞的线程(sleepwaitjoin)的线程,线程会抛出InterruptedException异常。
	3.2、打断正常的线程,可以根据打断状态来标记是否退出线程。

线程安全

synchronized关键字底层原理

image-20241127150507238

image-20241127150709966

image-20241127150954256

面试总结

synchronized关键字的底层原理?
1Synchronized【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】
2、它的底层由monitor实现的,monitor是JVM级别的对象(C++),线程获得锁需要使用对象(锁)关联monitor
3、在monitor内部有三个属性,分别是owner、entrylist、waitset
4、其中owner是关联的获得锁的线程,并且只能关联一个线程;entrylist关联的是处于阻塞状态的线程;waitset关联的是处于Waiting状态的线程。

synchronized关键字底层原理-进阶

image-20241127151421772

image-20241127151605503

image-20241127151837195

image-20241127152113453

image-20241127152407609

image-20241127152647076

image-20241127152712388

image-20241127152933415

image-20241127153054658

image-20241127153248255

谈谈JMM(Java内存模型)

image-20241127222206753

image-20241127225301236

对CAS 的理解

image-20241127225443417

image-20241127225742255

image-20241127231201460

image-20241127231333910

面试总结

CAS你知道吗?
	CAS:它体现的是一种乐观锁的思想,在无锁状态下保证线程操作数据的原子性
	CAS使用的地方很多:AQS框架、AtomicXXX类
	在操作共享变量时使用自旋锁,效率上更高一些
	CAS底层是调用Unsafe类中的方法,都是操作系统提供的,其他语言实现
	
乐观锁和悲观锁的区别?
	CAS是基于乐观锁思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗
	synchronized 是基于悲观锁的思想:最悲观的估计,得防着其他线程来修改共享变量,我上了锁你们都别想改,我改完解开锁,你们才有机会。

谈谈对volatile的理解

image-20241128195508735

image-20241128195759726

image-20241128200227841

image-20241128200428533

image-20241128200634868

面试总结

谈谈对volatile的理解?
1、保证线程间的可见性
	用volatile 修饰共享变量,能够防止编译器等优化发生,让一个线程对共享变量的修改对另一个线程可见。
2、禁止进行指令重排序
	指令重排:用volatile 修饰共享变量会在读、写共享变量时加入不同的屏障,阻止其他读写操作越过屏障,从而达到阻止重排序的效果。

什么是AQS?

image-20241128200933470

image-20241128201114922

image-20241128201210912

image-20241128201258898

面试总结

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

image-20241229200905172

ReentrantLock的实现原理

image-20241128201554982

image-20241128201705162

image-20241128201815035

面试总结

ReentrantLock的实现原理?
	ReentrantLock 表示支持重新进入的锁,调用Lock 方法获取了锁之后,再次调用Lock,是不会再阻塞
	ReentrantLock 主要利用 CAS+AQS 队列来实现
	支持公平锁和非公平锁,在提供的构造器中无参默认是非公平锁,也可以传参设置为公平锁。

synchronized和Lock有什么区别?

面试总结

synchronized 和 Lock 有什么区别?
1、语法层面
	synchronized 是关键字,源码在JVM中,用C++ 语言实现
	Lock 是接口,源码由jdk提供,用java语言实现
	使用synchronized时,退出同步代码块,锁会自动释放,而使用Lock时,需要手动调用unlock 方法释放锁
2、功能层面
	二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
	Lock 提供了许多 synchronized 不具备的功能,例如公平锁、可打断、可超时、多条件变量
	Lock 有适合不同场景的实现,如ReentrantLock,ReentrantReadWriteLock(读写锁)
3、性能层面
	在没有竞争时,synchronized 做了很多优化,如偏向锁、轻量级锁,性能不赖
	在竞争激烈时,Lock的实现通常会提供更好的性能。

死锁产生的条件是什么?

image-20241128202827666

image-20241128203003334

image-20241128203026537

image-20241128203207326

面试总结

死锁产生的条件是什么?
	一个线程需要同时获取多把锁,这时就容易发生死锁
	
如何诊断死锁?
	当程序出现了死锁现象,我们可以使用jdk自带的工具:jps jstack
		jps:输出JVM中运行的进程状态信息
		jstack:查看java进程内线程的堆栈信息,查看日志,检查是否有死锁
				如果有死锁现象,需要查看具体代码分析后,可修复
	可视化工具jconsole、VisualVM 也可以检查死锁问题

ConcurrentHashMap 说一下

image-20241128203414793

image-20241128203445632

image-20241128203614560

image-20241128203738236

面试总结

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

导致并发程序出现问题的根本原因是什么?

image-20241128204014056

image-20241128204054997

image-20241128204142958

image-20241128204235444

image-20241128204309873

面试总结

导致并发程序出现问题的根本原因是什么?
	1、原子性		synchronized、Lock
	2、内存可见性	   volatilesynchronized、Lock
	3、有序性		volatile

线程池

说一下线程池的核心参数(线程池的执行原理)

image-20241129143745472

面试总结

线程池的核心参数?
1、corePoolSize:核心线程数目
2、maximumPoolSize:最大线程数目
3、KeepAliveTime:救急线程的生存时间
4、unit:救急线程的生存时间单位
5、workQueue:当没有空闲核心线程时,新来任务会加入到此队列排队,队列满时会创建救急线程执				行任务
6、threadFactory:可以定制线程对象的创建,例如名字
7、handler拒绝策略:当所有线程都在忙,workQueue也放满时,会触发拒绝策略


拒绝策略?
1、AbortPolicy:直接抛出异常,默认策略
2、CallerRunsPolicy:用调用者所在的线程来执行任务
3DiscardOldestPolicy:丢弃阻塞队列中最旧的任务,将新任务添加到任务队列
4DiscardPolicy:直接丢弃任务

image-20241129143914405

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

image-20241129144610496

面试总结

线程池中有哪些阻塞队列?
	1、ArrayBlockingQueue:基于数组,FIFO
	2、LinkedBlockingQueue:基于链表,FIFO

image-20241129144806740

如何确定核心线程数

image-20241129144950776

面试总结

image-20241129145047034

线程池的种类有哪些

image-20241129145302281

image-20241129145615251

image-20241129145833964

image-20241129150016995

面试总结

线程池的种类有哪些?
	1、newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
	2、newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务
	3、newCacheThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
	4、newScheduleThreadPool:可以执行延迟任务的线程池,支持定时及周期性任务执行

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

面试总结

image-20241129150352625

使用场景

线程池使用场景(项目中哪里用到线程池)

image-20241129150633492

image-20241129150717791

image-20241129150858410

image-20241129151748388

image-20241129151847743

image-20241129152050972

面试总结

image-20241129152323604

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

image-20241129152443185

image-20241129152621787

面试总结

image-20241129152645563

谈谈对ThreadLocal的理解

image-20241129152800720

image-20241129152907971

image-20241129153010342

image-20241129153139310

image-20241129153203754

image-20241129153325572

image-20241129153433553

面试总结

谈谈对ThreadLocal 的理解?
1、ThreadLocal 可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发线程安全问题。
2、ThreadLocal 同时实现了线程内的资源共享
3、每个线程内有一个ThreadLocalMap 类型的成员变量,用来存储资源对象
	a)调用set方法,就是以ThreadLocal自己作为key,资源对象作为value,放入当前线程的ThreadLocalMap集合中
	b)调用get方法,就是以ThreadLocal自己作为key,到当前线程中查找关联的资源值
	c)调用remove方法,就是以ThreadLocal自己作为key,移除当前线程关联的资源值
4、ThreadLocal内存泄漏问题
	ThreadLocalMap 中的key是弱引用,值是强引用;key会被GC释放内存,关联value的内存并不会释放。建议主动remove释放key,value
posted @   墨羽寻觅  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示