第五章多线程.pptx.ppt

上传人:本田雅阁 文档编号:3413528 上传时间:2019-08-23 格式:PPT 页数:48 大小:1.81MB
返回 下载 相关 举报
第五章多线程.pptx.ppt_第1页
第1页 / 共48页
第五章多线程.pptx.ppt_第2页
第2页 / 共48页
第五章多线程.pptx.ppt_第3页
第3页 / 共48页
第五章多线程.pptx.ppt_第4页
第4页 / 共48页
第五章多线程.pptx.ppt_第5页
第5页 / 共48页
点击查看更多>>
资源描述

《第五章多线程.pptx.ppt》由会员分享,可在线阅读,更多相关《第五章多线程.pptx.ppt(48页珍藏版)》请在三一文库上搜索。

1、第五章 多线程编程,刘亮亮,本章内容介绍, 上一章介绍了介绍面向对象程序设计的重,要的概念:继承。本章介绍多线程的概念。, 本章主要内容:, 多线程的概念 多线程的编程,第五章 多线程编程,5.1 概述,5.2 多线程的概念,5.3 多线程的创建方法 5.4 同步,5.5 中断线程,5.6 线程的状态,5.1 概述, 操作系统的多任务:同一时刻多个程序同时运,行;(操作系统将CPU时间划分成小的片段, 将其分配给不同的程序执行), 实现多任务的方法:, 抢占式多任务:直接中断而不需要事先和被中断程,序协商;例如:Windows操作系统等, 协作多任务(非抢占式):被中断程序同意交出控,制权之后

2、才能执行中断;例如:手机操作系统, 注:抢占式多任务更有效,更难实现,5.1 概述, 多线程程序在更低的层次上引入了多任务,而扩展了多任务的思想:单个程序看起来 可以同时处理多个任务。每个任务称为一 个线程。, 可以一次运行多个线程的程序被称为多线,程的;,5.1 概述, 多线程与多进程的区别?, 多进程:每个进程都有自己的变量的完备集; 多线程:线程间共享相同的数据;, 多线程的优点:, 共享的变量使得线程之间的通信比进程间的通信更,有效更简单;, 线程比进程更“轻量级”,创建和销毁单个线程比,发起进程的开销要小的多;, 多线程的实际应用:浏览器可以同时下载多幅,图片、Web服务器能够处理并

3、发的请求、Java 程序使用了一个线程在后台处理垃圾回收等等,5.2 多线程的概念, 进程:正在运行中的程序, 线程:就是进程中一个负责程序执行的控,制单元(执行路径), 一个进程中可以有多个执行路径,称为多,线程;, 开启多个线程是为了同时运行多部分代码 每一个线程都有自己运行的代码,这个内容是,多线程要执行的任务。,5.2 多线程的概念, 多线程的好处:解决了多部分同时运行的,问题;, 多线程的弊端:线程太多回收效率的降低; 应用程序的执行都是CPU在做随机的快速的,切换。,5.2 多线程的概念, JVM启动时候启动了多个线程:, 1、执行main函数的线程, 该线程的任务代码都定义在ma

4、in函数中;, 2、负责垃圾回收的线程, 参考:Object类中的finalize()方法进行垃圾回收,通过,System.gc()调用垃圾回收器。, 示例代码:ThreadDemo.java,5.3 多线程程序创建方法, ,主线程程序创建过程: 示例代码:ThreadDemo2.java 创建线程方法一:继承Thread类 创建步骤: 定义一个类继承Thread类; 覆盖Thread类中的run方法; 直接创建Thread的子类对象创建线程。 利用start方法开启线程(自动调用run方法);,5.2 多线程程序创建方法, 代码:,d1.start(); d2.start();,System

5、.out.println(“Hello World“);, 什么效果?为什么?, 因为开启了三个线程;, 如何区分执行的线程?, Thread类中的构造函数, 可以通过Thread类中的getName方法获取线程的名,称;( Thread.currentThread() 取得当前线程),5.2 多线程程序创建方法, 在主线程中设置出现异常代码:,d1.start(); d2.start();,System.out.println(1/0);, 看执行结果?, 示例2:在run中设置异常代码,例如数组越,界异常,看执行结果?, 结论:异常不会影响其它线程的执行。,5.3 多线程程序创建方法, 创

6、建方法二:实现Runnable接口, 步骤:, 1、定义类实现Runnable接口;, 2、覆盖接口中的run方法,将线程的任务代码封装到run,方法中;, 3、通过Thread类创建线程对象,并将Runable接口的子类,对象作为Thread类的构造函数的参数进行传递;, Note:参考JDK文档中的Thread类的构造函数,线程的任务封,装在Runable接口子类对象的run方法中,因此创建线程对象 时候必须明确声明要运行的任务;, 4、调用线程对象的start方法开启线程;, 示例代码:ThreadDemo3.java,5.3 多线程程序创建方法, 实现Runnable接口的好处:, 将

7、线程的任务从线程的子类中分离处理,进行,了单独的封装,利用面向对象的思想将任务的 封装成对象。, 避免了java单继承的问题;, 结论:创建线程多采用实现Runnable接口的,方法来创建。,5.3 多线程程序创建方法, Thread(Runable target), 构造一个新的线程来调用指定target的run方法, void start(), 启动这个线程,将引发调用run方法,这个方法将立即,返回,并且新线程将并发执行;, void run(), 调用关联Runable的run方法;, 注意:不要调用Thread类或Runable对象的run方法,,直接调用run方法只会在当前线程中执

8、行任务,并不 会启动新的线程,正确的做法是调用Thread的start() 方法,它会创建一个新的线程执行run方法。,多线程程序示例, 多线程示例一:实现一简单的售票程序, 实现步骤:,1、建立Ticket类(继承Thread或实现Runnable); 2、封装卖票函数sale为多线程任务; 3、开启4个线程进行买票, 问题一:采用Thread继承,票总数用static?, 问题二:如果不用static呢?(现实中,可以开设多个线程卖一,种类型的票,开另外几个线程卖另外一种类型的票,因此不能 将票数设置为static), 解决方法:实现Runnable接口,声明一个Ticket对象,开多个,

9、Thread来运行,共享内存数据, 问题三:在输出加Thread.sleep(10)(需要捕获异常)查看会出,现什么问题?, 为什么?这是个什么问题?,线程安全问题,如何解决?, 示例代码:TicketDemo.java,5.4 同步, 线程安全问题产生的原因:, 多个线程在操作共享的数据;, 操作共享数据的线程代码有多条;, 当一个线程在执行操作共享数据的多条代码过程中,,其他线程参与了运行,就会导致线程安全问题的产 生;, 解决思路:, 将多条操作共享数据的线程代码封装起来,当线程,执行这些代码的时候,其他线程不可以参与运算的, 必须要当前线程把这些代码执行完后,其他线程才 可以参与运算。

10、,5.4.1 同步代码块, 解决方法一:同步代码块,格式:synchronized(对象),/需要同步的代码, 示例:TicketDemo.java, 同步的好处:解决了线程的安全问题;, 同步的弊端:相对降低了效率,因为同步外的,线程的都会判断同步锁;,5.4.1 同步代码块, 同步的前提:, 必须有多个线程并使用同一个锁;, 例如:将买票中的锁(Object obj=new Object)定义在,run方法中,看会出现什么问题?,void run() ,Object obj=new Object(); While(true) ,Synchronized(obj) ,/code, ,示例,

11、需求:银行存钱入金库,两个储户存钱三次,每次,存1000元, 步骤:,1、定义Bank类(域:账户总额、方法:存钱方法save(int,num));,2、定义储户实现Runnable方法:实现run方法,调用Bank,的save方法存钱三次;,3、定义两个储户线程进行存钱操作;,问题:是否存在线程安全问题?输出前加sleep 利用同步代码块来解决。 是否还存在其它的方式。, 代码示例:BankDemo.java,5.4.2 同步函数, 解决方法二:同步函数 函数利用synchronized修饰, 上例中:利用synchronized修饰save方法, 问题:同步代码块的同步锁是定义的对象obj

12、,同步函数的锁是,什么呢?, 同步函数使用的锁是this, 分析:将购票程序中的run方法设为同步函数,看看效果,分析为,什么?一个线程进入后一直不出来,同步函数设置不对,因 为while(true)不需要同步;, 解决方法:将同步的代码封装成同步函数,在run中调用; 通过购票程序中同时利用同步代码块和同步函数来售票解释同步,函数的锁是this,代码示例:TicketDemo2.java, 问题:如果同步函数为静态函数,那么同步代码块中锁用什,么?this.getClass()或类名.class(生成的字节码文件), 注:不要将不需要同步的代码设为同步;,5.4.3 同步代码块与同步函数的区

13、别, 同步函数与同步代码块的区别:, 同步函数的锁是固定的this;, 同步代码块的锁是任意的对象; 建议使用同步代码块;,自学内容:单例设计模式中的多线程问题。* 懒汉式的线程安全问题。,5.5 中断线程, 线程将在它的run方法返回时候同时终止; 可以使用stop方法来终止线程但这个方,法已经不使用了;, 可以采用interrupt方法终止一个线程;, 当interrupt在一个线程上调用时候,该线程,的中断状态会被置位这是一个布尔标识, 存在于线程中;, 每个线程应该不断的检查中断状态标识,,以判断线程是否应该被中断;,5.5 中断线程, 利用Thread.currentThread()

14、方法获取当前线程; 调用isInterrupted方法来判断;,while(!Thread.currentThread().isInterrupted() ,/执行的代码, 但是如果一个线程被阻塞,它就无法检查中断,状态,就产生InterruptedException异常:当一 个被阻塞的线程调用interrupt()方法,阻塞调 用(sleep或wait)就会被InterruptedException 异常终止;,利用如下形式:,5.5 中断线程, public void run() try .,while(!Thread.currentThread().isInterrup ted() /

15、执行的代码,/如果每次工作迭代之后都调用sleep() 方法,那么isInterrupted就不需要检 查了,因为在阻塞状态调用sleep就会 抛出异常。 public void run() , catch(InterruptedException e),/线程在阻塞时候被中断,finally,/, ,try . while() ,/执行的代码 Threadsleep();,catch(InterruptedExceptione),/线程在阻塞时候被中断, ,finally,/, ,5.6 线程的状态, ,线程的四种状态 New(新生) Runnable(可运行) Blocked(被阻塞) D

16、ead(死亡),5.6.1 新生线程, 使用new创建一个线程时候: 例如:new Thread(r);, 线程还没有开始运行,处于新生(new)状态 处于该状态时候,程序还没有运行线程中,的代码,5.6.2 可运行线程, 一旦调用了start方法,该线程就称为可运行,状态(Runnable);, 注意:一个可运行线程可能实际上在运行,,也可能没有运行 决定于操作系统为该线 程提供的运行时间。, 线程一旦运行,它不需要始终保持运行,,有时候会中断,目的是使其他线程获得运 行的机会;,5.6.3 被阻塞线程, 当发生以下任何一种情况时候,线程就进,入阻塞状态:, 线程通过调用sleep方法进入睡

17、眠状态;, 线程调用一个在I/O上被阻塞的操作,即该操作,在输入输出之前不会返回给它的调用者;, 线程试图得到一个锁,而该锁正被其他线程所,持有;, 线程在等待某个触发条件;, 有人调用了线程的suspend方法该方法已过,时,不用,5.6.4 死线程, run方法正常退出而自然死亡, 因为一个未捕获的异常终止了run方法而使,线程猝死;, 判断线程是在当前是否存活:isAlive方法, 如果线程可运行或阻塞,返回true;, 如果线程在new状态且不可运行或者线程死亡,了,返回false,5.6.5 线程的状态转化,通过以下途径之一,从阻塞状,态回到可运行状态:,1)线程被置于睡眠状态,且,

18、已经过了指定的毫秒数;,2)线程正在等待I/O操作的完,成,且该操作已经完成;,3)线程正等待另一个线程持,有的锁,且另一个线程已 经释放该锁的所有权;,4)线程正在等待某个触发条,件,且另外一个线程发出 了信号表明条件已经发生 了变化;,5)线程已经挂起,且有人调,用了它的resume方法; resume方法已过时,5.7 死锁, 回顾“购票程序”:, 在同步函数中加锁;, 同步块中调用同步函数; 会出现什么现象?死锁 解释这种现象的原因。, 示例:TicketDemo4.java, 注:Java编程语言中没有任何东西能够避免,或者打破这种死锁现象。你必须仔细设计 你的程序确保死锁不会发生。

19、,5.8 线程间通信, ,购票程序示例和存款程序示例:多个线程执行,但是执行是相同的任 务。 线程间通信:多个线程在处理多个资源,但是执行的任务却不相同; 例如:存款和取款 示例:设计一个简单多线程程序,一个线程在对某个资源进行赋值操 作,另一个线程对某个资源进行取操作; 步骤:, ,1、定义一个资源类 2、定义一个Input类实现Runnable接口 3、定义一个Output类实现Runnable接口 4、定义两个线程,一个负责输入 一个负责输出, ,示例代码:ResourceDemo.java 运行代码看效果,为什么?解决办法? 增加同步,再看效果,会有什么问题? 如何解决:每赋值一次,然

20、后输出一次?,5.8.1 等待唤醒机制, ResourceDemo例子解决办法:, 定义一个标记flag,如果为true,则输出;否则,进行输出;然后更改标记;, 问题:如果标记为true,而输入线程获得执,行权,如何做?, 解决:需要对当前线程进行冻结。采用,wait()函数。当执行完输出后要将输入线程 进行唤醒notify()。等待唤醒机制, 示例代码: ResourceDemo2.java,5.8.1 等待唤醒机制, wait():让线程处于冻结状态,被wait的线程会存储在,线程池中。, notify():唤醒线程池中一个线程(任意) notifyAll():唤醒线程池中的所有线程。,

21、 注意:这些方法都必须定义在同步代码或同步函数 中因为这些方法用于操作线程状态的方法,必,须要明确到底操作的是哪个锁上的线程。, 问题:为什么线程的操作方法wait等定义在Object中?, 因为这些方法是监视器的方法,监视器就是锁。锁可,以是任意的对象,任意的对象调用的方式一定定义在 Object类中,5.8.1 等待唤醒机制, 作业:,将ResourceDemo2例子改成合理的实现方式。,5.8.2 生产者-消费者, 生产者-消费者(producer-consumer)问题,也称作,有界缓冲区(bounded-buffer)问题,两个进程共享 一个公共的固定大小的缓冲区。其中一个是生产者,

22、 用于将消息放入缓冲区;另外一个是消费者,用于 从缓冲区中取出消息。, 示例:定义生产者线程和消费者线程,并模拟多次,生产和消费。, 一个生产者线程和一个消费者线程;(定义标记flag) 多个生产者和多个消费者:分析原因?, 等待的线程不会去判断标记,如果改为while(flag)会出,现什么问题?, 示例代码:ProduceConsumeDemo.java,5.8.2 生产者-消费者 如何解决多生产者-多消费者的问题? 出现的问题是因为唤醒多个线程,CPU执行权选择给同类线程导致 的;应该唤醒对方线程,但是没有办法唤醒指定的线程。 生产者与消费者模型中,要保证以下几点:, ,1 同一时间内只

23、能有一个生产者生产 2 同一时间内只能有一个消费者消费 3 生产者生产的同时消费者不能消费 4 消费者消费的同时生产者不能生产,生产方法加锁sychronized 消费方法加锁sychronized 生产方法加锁sychronized 消费方法加锁sychronized,5 共享空间空时消费者不能继续消费 消费前循环判断是否为空, 空的话将该线程wait,释放锁允许其他同步方法执行, 6 共享空间满时生产者不能继续生产 生产前循环判断是否为满, 满的话将该线程wait,释放锁允许其他同步方法执行,5.8.2 生产者-消费者, if判断标记:只判断一次,会导致不 该运行的线程运行了,出现了数据错

24、 误的情况,线程不安全; 解决方法:While判断标记,解决了线 程获取执行权后,是否要运行;,Resource,t0 t1,t2 t3, notify:只能唤醒一个线程,如果唤醒 的是同方线程,如果利用while判断标 记就会出现死锁; notifyAll:把所有的线程都唤醒,解决 了死锁的问题。 示例代码: ProduceConsumeDemo.java 问题:效率问题,每次唤醒所有的线 程,需要判断标记,效率低,能否唤 醒只对方线程?,线程池 分析:wait的线程会放入到线程 池中。如果flag=false,t2,t3会放入到 线程池中,t0生成完,flag=true, T0等待,放入线

25、程池,唤醒t1, 此时flag为true,t1也等待,放入 线程池,这样就没有唤醒的线程, 导致死锁。,5.8.3 ReentranLock对象, synchronized关键字自动提供了一个锁和关联,的“条件”。 JDK5.0以后,引进了 ReentranLock类。结构如下:,Lock myLock=new ReentranLock(); myLock.lock(); try ,/同步代码,finally ,myLock.unlock();/释放锁,5.8.3 ReentrantLock对象, 将多生产者和多消费者程序改用ReentrantLock,对象来实现:, Condition co

26、n= myLock.newCondition();/利用当,前锁来获得当前“条件”对象监视器, Condition类:, await():造成当前线程在接到信号或被中断之前一,直处于等待状态。, signal(),唤醒一个等待线程, signalAll(),唤醒所有等待线程。,5.8.3 ReentrantLock对象, 用法:, Lock myLock=new ReentrantLock();, Condition con= myLock.newCondition(); con.await();, con.signalAll();, 利用ReentrantLock与Condition实现代码

27、:, ProduceConsumeDemo2.java, 分析与同步代码块的区别?, 还是没有解决唤醒对方线程,如何做?,5.8.3 ReentrantLock对象 解决方法:用相同的锁建议定义两个监视器对象:生产 者监视器、消费者监视器 代码: Condition producer_con=lock.newCondition(); Condition consumer_con=lock.newCondition();, ,/生产者代码中 生产者监视器使当前线程等待: producer_con.await(); 消费者监视器唤醒消费者线程: Consumer_con.signal(); 反之一

28、样, 示例代码: ProduceConsumeDemo3.java,5.8.3 ReentrantLock对象, Lock接口:出现替代了同步代码块或同步函数,,将同步的隐式锁操作变成现实锁操作。操作更 灵活,可以一个锁加多组监视器;, lock:获取锁, Unlock:释放锁,通常需要定义在finally代码块中;, Condition接口:替代了Object了Object中的wait、,notify、notifyAll方法。将这些监视器方法单独 进行了封装,变成了Condition监视器对象。, await(): signal():, signalAll():,5.8.3 wait和sle

29、ep的区别, Wait可以指定时间也可以不指定; Sleep必须指定时间;, 在同步中,对cpu的执行权和锁的处理不同:, Wait:释放执行权,释放锁 Sleep:释放执行权,不释放锁,5.9 线程中的其它方法*, 中断线程interrupt方法:, 中断线程两种方法:一种定义标记,判断,标记来中断线程;另一种通过interrupt方法 进行中断线程;, 守护线程方法:setDaemon(boolean on)方,法,也可以认为是后台线程。, , 自己参考jdk手册去使用这些方法,5.10 小结, ,本章介绍了Java多线程编程,主要内容: 多线程的概念 创建多线程的方法:Thread 和Runnable 线程安全问题同步代码或同步函数 生产者和消费者模型: 等待唤醒机制 ReentrantLock类和Condition类,作业, 编程练习:, 实现现实生活中的“生产者和消费者”的例子, 要求:多生产者与多消费者,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1