北京大学JAVA讲义-第七章 多线程_第1页
北京大学JAVA讲义-第七章 多线程_第2页
北京大学JAVA讲义-第七章 多线程_第3页
北京大学JAVA讲义-第七章 多线程_第4页
北京大学JAVA讲义-第七章 多线程_第5页
已阅读5页,还剩31页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

JavaProgramming第七章多线程北京大学·Java程序设计课程讲义Contents本章知识框架Java多线程编程核心知识体系,从基础概念到并发安全的完整脉络。01进程与线程基础概念02线程生命周期与六大状态03线程创建的三种方式04线程同步机制与锁05线程间通信机制06线程池与Callable接口07并发安全集合框架08高频面试题与核心总结Chapter01进程与线程基础概念从操作系统视角理解并发编程的核心单元Chapter07·Multithreading进程与线程的核心概念对比进程是操作系统资源分配的基本单位,线程是CPU调度的最小执行单元。理解两者的包含关系和内存共享机制,是掌握Java多线程编程的认知基石。多应用程序窗口并发运行场景进程(Process)资源分配01操作系统中独立运行的应用程序实例,拥有独立的内存地址空间和系统资源02宏观并行、微观串行,通过CPU时间片轮转实现多任务并发03进程间通信成本较高,需要通过管道、消息队列、共享内存等IPC机制CPU多核心芯片微距特写线程(Thread)CPU调度01进程内的独立执行路径,被称为轻量级进程,共享所属进程的堆内存和方法区02每个线程拥有独立的栈空间用于存储局部变量和方法调用信息,实现执行隔离03线程切换开销远小于进程切换,是实现程序内部并发的高效手段第七章·多线程Java线程内存模型Java线程共享堆内存但各自拥有独立栈空间,这种"共享-私有"的内存架构既是多线程高效协作的基础,也是线程安全问题的根源所在。主线程由JVM自动创建,负责执行main()方法,是程序的起始执行入口main()栈内存线程私有,每个线程独立维护方法调用栈和局部变量,互不干扰Private堆内存所有线程共享,通过new创建的对象存放于此,多线程可同时访问同一对象Shared方法区存储类信息、常量和静态变量,所有线程共享,需注意线程安全问题Thread-safe服务器内存条硬件·物理内存架构Chapter07·多线程线程底层实现原理Java线程采用1:1内核级线程模型,由JVM创建但由操作系统调度。理解CPU时间分片、线程模型分类以及Java线程与OS内核线程的映射关系,有助于深入理解并发编程的本质。硬件层01多核CPU可真正并行执行多线程,单核通过时间分片(如10ms切换)实现宏观并行微观串行10ms·时间分片OS层02操作系统负责线程创建、调度和销毁,提供用户级、内核级和混合级三种线程实现模型3种线程模型JVM线程模型03Java线程基于1:1内核级线程模型,每个java.lang.Thread对应一个OS内核线程1:1·映射关系JVM线程模型04JVM管理线程生命周期(如start()启动),但调度权完全交给操作系统,JVM无法干预CPU分配OSKERNEL·调度权Chapter02线程生命周期与六大状态掌握线程从创建到终止的完整状态流转图Chapter07·Multithreading线程六大生命周期状态Java线程经历New→Ready→Running→阻塞/等待→Terminated的完整生命周期。理解每个状态的触发条件和转换路径,是分析多线程程序行为和排查并发Bug的关键基础。New(新建)通过newThread()创建线程对象,仅分配内存资源,尚未启动执行。newThread()Ready(就绪)调用start()方法后进入就绪队列,等待操作系统分配CPU时间片。start()Running(运行)获得CPU时间片执行run()方法,时间片用完后回到Ready状态重新排队。run()Blocked(阻塞)尝试获取synchronized同步锁失败,进入锁等待队列直到锁被释放。synchronizedTimedWaiting(限时等待)调用sleep(ms)进入固定时长休眠,到期后自动恢复到Ready状态。sleep(ms)Terminated(终止)run()执行完毕或发生未捕获异常,线程彻底消亡且不可复用。endoflifeMULTITHREADING核心面试考点:sleep()vswait()对比sleep()与wait()虽都能暂停线程,但在所属类、锁释放行为、唤醒机制和使用场景四个维度存在本质差异。这是Java并发编程最经典的面试题之一,也是实际开发中正确使用线程等待的前提。sleep()方法属于Thread类的静态方法,调用方式为Thread.sleep(毫秒数)限时休眠期间不释放已持有的同步锁,其他线程仍无法获取该锁到期后自动恢复到Ready就绪状态,无需外部唤醒,可在任意位置调用wait()方法属于Object类的实例方法,任意Java对象都可调用调用后立即释放当前持有的同步锁和CPU资源,允许其他线程获取锁必须在synchronized同步代码块内使用,需要notify()唤醒才能恢复Chapter07·Multithreading线程状态转换的关键路径线程状态转换由锁竞争、休眠等待、CPU调度三大机制驱动。掌握每条转换路径的触发条件和恢复机制,是分析死锁、饥饿等并发问题的理论基础。Ready→Running调度器从就绪队列选中线程,分配CPU时间片Running→Ready时间片用尽,线程让出CPU排入就绪队列末尾Running→Blocked获取synchronized锁失败,进入锁等待队列Blocked→Ready锁释放后等待线程竞争成功,恢复就绪状态Running→Terminatedrun()执行完毕或抛出未捕获异常,生命周期结束多线程并发编程·程序员工作场景Chapter03线程创建的三种方式Thread继承、Runnable接口与Callable接口的实现对比CHAPTER7·MULTITHREADING三种线程创建方式概览Java提供继承Thread类、实现Runnable接口和实现Callable接口三种线程创建方式。Runnable因避免单继承限制而最常用,Callable因支持返回值而在异步编程中不可替代。01继承Thread类子类重写run(),通过new子类对象.start()启动,写法简单但受单继承限制02实现Runnable接口实现run()后将实例传入Thread构造器,避免单继承问题且支持多实例共享03实现Callable接口call()支持泛型返回值和异常抛出,配合FutureTask获取异步执行结果04关键区别start()由JVM创建新线程并调用run();直接调用run()仅是普通方法调用三种创建方式核心对比对比维度继承ThreadRunnable接口Callable接口核心方法run()run()call()返回值无无有(泛型V)异常声明不可抛不可抛可抛出异常继承限制无法再继承可继承其他类可继承其他类典型搭配直接使用Thread构造器FutureTask+线程池Runnable最灵活、Callable支持返回值,Thread类继承方式因单继承限制使用最少CHAPTER07·MULTITHREADINGThread类与Runnable接口实现详解继承Thread类与实现Runnable接口是最基础的两种线程创建方式。Runnable接口因解耦任务与线程、避免单继承限制、支持资源共享三大优势,成为实际开发中的首选方案。继承Thread类01创建Thread子类并重写run()方法在run()方法内编写线程要执行的业务逻辑代码,这是线程执行的核心入口02通过start()方法启动线程new子类对象后调用start(),JVM自动在新线程中执行run()方法,而非直接调用run()03局限:单继承限制扩展性Java单继承机制导致该类无法再继承其他父类,若需继承其他类则此方式受限实现Runnable接口01实现run()并传入Thread构造器实现Runnable接口的run()方法,将实现类实例作为参数传入Thread构造器创建线程02天然支持资源共享多个Thread对象可共享同一个Runnable实例,适合多线程操作同一资源的场景,如售票系统03任务与线程解耦,扩展性更强任务逻辑与线程机制分离,实现类仍可继承其他类或实现更多接口,符合面向对象设计原则JAVAMULTITHREADING·07Callable接口与Future异步结果Callable接口通过call()方法实现了带返回值和异常声明的线程任务,配合Future/FutureTask实现异步结果获取。这是Java从基础多线程迈向异步编程的关键桥梁。01Callable<V>泛型返回值:call()方法具有泛型返回值V和异常抛出能力,弥补了Runnable无返回值的不足02FutureTask双重封装:封装Callable实例,既是Runnable的实现可被Thread执行,又是Future可获取结果03Future.get()阻塞等待:阻塞当前线程直到异步任务完成并返回结果,适用于需要等待子线程结果的场景04异步调用模式:主线程提交Callable任务后继续执行其他逻辑,需要时再通过Future获取结果,提升整体效率异步编程场景:多屏幕协作的开发工作流CHAPTER04线程同步机制与锁从synchronized到ReentrantLock,掌握并发安全的核心工具Chapter07·Multithreading线程安全问题的本质线程安全问题源于多个线程对共享临界资源的非原子操作产生竞态条件。理解"临界资源-原子操作-竞态条件"三者关系,是设计正确同步方案的认知前提。银行ATM取款——并发竞态的经典实证场景临界资源:多线程共享访问的同一对象或变量,如账户余额、计数器、共享集合等原子操作:不可被线程调度中断的最小执行单元,保证"要么全部完成、要么完全不执行"竞态条件:多线程对临界资源的非原子操作导致最终结果依赖执行顺序,产生不可预期的数据错乱经典案例:两个线程同时读取余额1000元并各取800元,无同步时均判断余额充足,透支至-600元第七章·多线程synchronized同步机制详解synchronized是Java内置的互斥同步原语,通过对象锁机制保证临界区代码的原子性。同步代码块可精确控制锁粒度,同步方法则对整个方法体加锁。同步代码块synchronized(obj){...}语法为synchronized(临界资源对象){...},仅锁定指定对象而非整个方法获取到锁标记的线程执行代码块,未获取到的线程进入Blocked阻塞状态等待释放锁粒度可精确控制,仅包裹必要的共享资源操作代码,减少锁持有时间提升并发性能细粒度精确锁定临界资源对象同步方法synchronizedvoidmethod(){...}方法声明前加synchronized修饰符,实例方法等价于synchronized(this)锁定当前对象静态同步方法锁定当前类的Class对象,所有该类实例共享同一把类级别锁锁粒度较粗,整个方法体为同步区域,适用于方法内大部分代码都需要互斥的场景粗粒度整个方法体互斥执行JUC·ConcurrencyReentrantLock显式锁机制ReentrantLock是JUC包提供的可重入互斥锁,相比synchronized支持公平锁选择、可中断获取和超时尝试等高级特性。使用时必须在finally块中释放锁以防止死锁,是复杂并发场景的首选同步工具。手动加锁释放—lock()/unlock()必须在finally块中确保释放,防止死锁公平与非公平模式—FIFO公平锁减少饥饿;非公平锁允许插队,吞吐量更高可中断获取锁—lockInterruptibly()等待线程响应中断信号,避免无限期阻塞超时尝试获取—tryLock(time)指定时间内尝试,失败立即返回false而非阻塞精密锁芯结构—锁的精细控制隐喻CONCURRENCY·LOCKMECHANISM读写锁ReentrantReadWriteLock读写锁通过分离读操作和写操作的互斥策略,在读多写少场景下显著提升并发性能。读读不互斥、读写/写读/写写互斥的设计,是缓存系统、配置中心等高频读取场景的理想同步方案。读锁(共享锁)多个线程可同时持有读锁进行并发读取,读操作之间不互斥,最大化读吞吐量。并发读写锁(独占锁)写操作与所有其他操作互斥,获取写锁时阻塞所有读线程和其他写线程。全互斥典型应用场景缓存系统中读请求远多于写更新,使用读写锁可将并发读性能提升数倍至数十倍。数十倍锁降级机制持有写锁的线程可在不释放写锁的情况下获取读锁,实现写→读的安全降级。写→读CHAPTER07·MULTITHREADINGsynchronized与Lock综合对比synchronized以简洁著称、由JVM自动管理,Lock以灵活见长、需手动控制。两者在JDK6优化后性能差距已大幅缩小,开发者应根据场景复杂度选择合适的同步工具。对比维度synchronizedLock(ReentrantLock)实现层面JVM关键字,字节码层面JUCAPI接口,Java代码层面锁管理自动获取和释放手动lock()和unlock()公平锁支持仅非公平锁支持公平与非公平两种模式可中断/超时不支持支持lockInterruptibly和tryLock条件变量单一wait/notify多个Condition分组通知适用场景简单同步,代码简洁优先复杂并发控制,灵活性优先synchronized胜在简洁自动,Lock胜在灵活可控,JDK6后两者性能差距已显著缩小CHAPTER05线程间通信机制wait/notify协作模式与生产者-消费者模型第七章·多线程wait/notify/notifyAll通信方法Object类的wait/notify/notifyAll是Java线程间协作的基础机制,必须在同步代码块内使用。理解等待集的运作原理和锁释放时机,是实现正确线程协作的关键。接力赛交接棒——线程间wait/notify协作传递的隐喻01wait():当前线程释放对象锁和CPU资源,进入该对象的等待集(waitset)无限等待直到被唤醒02notify():从对象等待集中随机唤醒一个线程,被唤醒线程需等待当前线程释放锁后才能竞争获取03notifyAll():唤醒对象等待集中的所有等待线程,所有被唤醒线程竞争对象锁,适合多消费者场景04关键细节:notify/notifyAll调用后不立即释放锁,当前线程执行完同步代码块后锁才释放给被唤醒线程第七章·多线程经典模型:生产者-消费者协作生产者-消费者模型是线程间通信的标准范式,通过"缓冲区满则等待、空则唤醒"的条件协作机制,实现了生产与消费线程的解耦和负载均衡,是消息队列、任务调度等系统的设计原型。生产者线程持续生产数据并放入共享缓冲区,缓冲区满时调用wait()等待消费者消费成功放入数据后调用notify()唤醒可能在等待的消费者线程继续消费wait()/notify()消费者线程持续从共享缓冲区取出数据消费,缓冲区空时调用wait()等待生产者生产成功取出数据后调用notify()唤醒可能在等待的生产者线程继续生产BlockingQueue工程化实现JUC的BlockingQueue封装了完整的生产者-消费者逻辑,ArrayBlockingQueue和LinkedBlockingQueue最常用Kafka消息队列、线程池任务队列都是该模型的工程化应用Kafka·ThreadPoolCHAPTER06线程池与并发工具从Executors工厂到自定义线程池的工程实践CONCURRENCY·并发编程线程池核心概念与API体系线程池通过复用线程、控制并发数量、统一管理生命周期三大优势,成为企业级Java开发的标准并发工具。ExecutorService接口是线程池的核心API,提供了任务提交和池管理的完整能力。服务器集群—线程池统一管理多个线程的隐喻核心优势避免频繁创建销毁线程的OS级开销,控制最大并发数防止资源耗尽,统一线程管理ExecutorJUC包顶层根接口,定义了execute(Runnable)基础任务提交方法ExecutorService线程池常用接口,提供submit(Runnable/Callable)提交任务和shutdown()关闭池Future返回值submit(Callable)返回Future对象,主线程可通过Future.get()异步获取结果Chapter7·MultithreadingExecutors工厂方法与线程池类型Executors提供四种快速创建线程池的工厂方法,各有适用场景。但阿里规范禁止在生产环境使用Executors,因为newFixedThreadPool和newSingleThreadExecutor的无界队列、newCachedThreadPool的无限线程数均存在OOM风险,推荐手动创建ThreadPoolExecutor。常用线程池类型newFixedThreadPool(n)固定n个线程,任务多时排入LinkedBlockingQueue无界队列等待newCachedThreadPool()弹性扩容无上限,空闲60秒回收,适合大量短时异步任务newSingleThreadExecutor()单线程串行执行所有任务,保证任务按提交顺序完成生产环境注意事项阿里规范禁止使用Executors无界队列和无限线程数可导致内存溢出OOM推荐ThreadPoolExecutor手动创建明确设置核心线程数、最大线程数、队列容量和拒绝策略JAVACONCURRENCY·第七章ThreadPoolExecutor七大核心参数ThreadPoolExecutor的七大参数精确控制线程池的运行行为。理解"核心线程→队列→非核心线程→拒绝策略"的任务处理流程,是合理配置线程池和排查并发问题的基础。corePoolSize&maximumPoolSize分别定义常驻核心线程数和线程数上限,控制并发规模。并发规模回收&队列keepAliveTime控制非核心线程空闲回收;workQueue有界可控风险,无界有OOM隐患。资源控制任务提交流程与参数对应步骤判断条件执行动作第一步线程<core创建核心线程第二步核心满且队列未满入workQueue第三步队列满且<max创建非核心线程第四步已达maxPoolhandler拒绝任务按"核心线程→队列→非核心线程→拒绝策略"优先级依次处理CHAPTER07并发安全集合框架从Vector到ConcurrentHashMap的并发集合演进之路ConcurrencyCollections·EvolutionJava并发集合三代演进Java并发集合从非安全集合、全量锁集合演进到JUC高效并发集合,核心思路是从"粗粒度全锁"走向"细粒度分锁"乃至"无锁CAS"。理解这一演进脉络,有助于在不同场景中选择最合适的并发集合。01第一代:非安全集合ArrayList、HashMap、HashSet等标准集合无任何同步机制,单线程性能最优。多线程并发读写时可能出现数据覆盖、死循环(HashMap扩容)等严重问题。无同步机制02第二代:全量锁集合Vector、Hashtable对所有方法加synchronized,线程安全但同一时刻仅一个线程可操作。粗粒度全锁导致并发性能极差,在现代高并发场景中已逐步淘汰。synchronized03第三代:JUC高效集合CopyOnWriteArrayList写时复制、ConcurrentHashMap分段锁、CAS无锁队列等精细化并发策略。在保证线程安全的前提下,并发性能提升数倍至数十倍,是现代Java开发的标准选择。CAS无锁JavaConcurrentCollections核心JUC集合:COW与CHM详解CopyOnWriteArrayList以写时复制实现读无锁,ConcurrentHashMap以分段锁/CAS实现细粒度并发。两者分别代表"读多写少"和"通用高并发"两种场景的最优解。CopyOnWriteArrayList写时复制·读无锁读性能极高写性能较低锁粒度全量复制典型场景读多写少ConcurrentHashMap分段锁/CAS·细粒度读写性能均衡高效锁粒度Node级null限制不允许典型场景通用高并发01COW写操作—复制整个底层数组,在新副本上修改后替换引用,读操作直接访问原数组无需加锁02COW读性能—接近ArrayList,写操作因全量复制开销大,适合读多写少场景如事件监听列表03CHM分段锁—JDK7采用Segment机制,数据分多段各自加锁,不同段操作互不干扰提升并发度04CHM优化—JDK8采用CAS+synchronized的Node级锁,锁粒度更细,不允许key/value为nullCHAPTER07·MULTITHREADING阻塞队列与无锁队列BlockingQueue通过put/take的内置阻塞机制简化了生产者-消费者模型的实现,ArrayBlockingQueue和LinkedBlockingQueue是最常用的两种实现。ConcurrentLinkedQueue则以CAS无锁算法实现高吞吐非阻塞队列。物流传送带——阻塞队列生产消费流程的物理隐喻ArrayBlockingQueue基于数组的有界阻塞队列,创建时必须指定容量,满时阻塞生产者、空时阻塞消费者有界·数组LinkedBlockingQueue基于链表的可选有界阻塞队列,不指定容量则近似无界(Integer.MAX_VALUE)可选有界·链表put()/take()put在队列满时阻塞等待空间,take在队列空时阻塞等待数据,天然适配生产者-消费者模型阻塞原语ConcurrentLinkedQueue基于CAS无锁算法的线程安全非阻塞队列,适合高吞吐量但不需要阻塞的场景无锁·CASCHAPTER08高频面试题与核心总结系统梳理本章核心考点与常见面试陷阱JavaMultithreading·InterviewEssentials高频面试题精讲(基础篇)进程线程区别、sleep与wait对比、线程创建方式、synchronized与Lock差异是Java多线程面试的四大基础必考题。准确理解每个考点的多维度差异,是面试成功的基本保障。01进程vs线程进程是资源分配单位,拥有独立内存空间;线程是CPU调度单位,共享堆但拥有独立栈。两者切换开销差异显著。资源

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论