1 / 75

第 6 章 Java 多线程

第 6 章 Java 多线程. 主要内容 线程基本概念 线程状态及生命周期 Java 线程的创建 Thread 类和 Runnable 接口 线程调度与优先级 线程的控制和消息传递 线程同步和死锁 守护线程 ( Daemon ). 程序、进程与线程. 程序与进程 程序( program ) 是一段静态的代码文件,它是应用软件执行的 ( 描述 ) 蓝本。 进程( Process ) 是程序的一次动态执行过程,它依赖于操作系统,从分配内存、加载代码、执行相应操作至完成任务的一个完整过程。 这个过程本身也是动态的:从产生、运行、发展至死亡的过程。. 线程

elle
Download Presentation

第 6 章 Java 多线程

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第6章 Java多线程 主要内容 • 线程基本概念 • 线程状态及生命周期 • Java线程的创建 • Thread类和Runnable接口 • 线程调度与优先级 • 线程的控制和消息传递 • 线程同步和死锁 • 守护线程 (Daemon)

  2. 程序、进程与线程 • 程序与进程 • 程序(program) • 是一段静态的代码文件,它是应用软件执行的(描述)蓝本。 • 进程(Process) • 是程序的一次动态执行过程,它依赖于操作系统,从分配内存、加载代码、执行相应操作至完成任务的一个完整过程。 • 这个过程本身也是动态的:从产生、运行、发展至死亡的过程。

  3. 线程 • 线程是程序内的控制流,与进程相似,但执行单位比进程更小。 • 线程也称为轻型进程,不同线程间允许任务协作和数据交换。 • 一个程序在其运行过程中,可以产生多个线程,形成多条执行线索。每条线索,即每个线程也有它自身的产生、存在和消亡的过程。

  4. 多线程 • 多线程是实现程序并发的一种有效手段。 • 一个进程可以通过运行多个线程来并发地执行多项任务。 • 多个线程如何调度执行由系统来实现。 • 线程是程序中的单个执行流,多线程是一个程序 中包含的多个同时运行的执行流

  5. 线程与进程比较 • 进程:内核级的实体。 每个进程有自己的状态、有自己的专用数据段(独立内存资源),包含虚存映象、文件指示符用户ID等。这些结构都在内核空间中,用户程序只有通过系统调用才能访问与改变。 • 线程:用户级的实体。 线程结构驻留在用户空间中,能够被普通的用户级函数组成的线程库直接访问。寄存器(栈指针,程序计数器)是线程专有的成分。线程间共享数据段 • 一个进程中的所有线程共享该进程的状态。

  6. 线程并发 • 并发性(Concurrency)是两个或多个线程(或传统的进程)可以同时在执行代码之中;可以是相同的代码,也可以是不同的代码。这些线程可以一次执行,也可以多次执行,即一个已开始执行但被中断,而另外一个已开始了。 • 但在给定的时间点上,只有一个在CPU在处理一个线程 。

  7. 线程并行 • 并行性(Parallelism)是针对多处理器环境而言的,是指两个或多个线程真正同时运行在不同的CPU上。 • 在多处理器机上,很多不同的线程可以并行运行,或者说是同时运行。

  8. Benefits of Threads • Takes less time to create a new thread than a process • Less time to terminate a thread than a process • Less time to switch between two threads within the same process • Since threads within the same process share memory and files, they can communicate with each other without invoking the kernel

  9. Multithreading Operating system supports multiple threads of execution within a single process

  10. How to Configure Threads • Kernel-Level Threads (KLTs) • User-Level Threads (ULTs) • Hybrid

  11. KLTs • Kernel maintains context information for both the process and the threads • Kernel provides thread control API for applications • Scheduling is done on a thread basis • Examples of this kind of thread include Win XP/Server, Linux, and OS/2

  12. ULTs • All thread management is done on the application level • It is supported by a thread libraryoutside the kernel e.g. pthread(POSIX thread) libraries, with all of the threadsmapping into a single kernellevel process • The kernel is not aware of the existence of threads

  13. Combine KLT and ULT Both KLT and ULT have their pro and con • By using hybrid strategy, the configuration can take advantages of both • If the parallelism of application is logical and needs no hardware support, then use the ULT • E.g. In a windowing system, all but one are idle • If it is a real parallelism, then use multiple KLT for performance gain • SMP can further enhance the performance by dispatch correlating threads to multiple processors simultaneously

  14. Hybrid Approaches • Thread creation done in the user space • Bulk of scheduling and synchronization of threads done in the user space • User’s threads are supported by a set of kernel threads • An good example is Solaris

  15. Solaris Thread And SMP Management Solaris takes the hybrid approach It makes use of four separate thread-related concepts • Processnormal UNIX process • User-level threads(ULTs) are implemented through a threads library in the address space of a process, these are invisible to the operating system. ULTs are the interface for application parallelism • Lightweight processes(LWP) a mapping between ULTs and kernel threads Each LWP supports one or more ULTs and can be visible within a process, hence LWP data structures exist within their respective process address space Each LWP is bound to a single dispatchable kernel thread, and the data structure for that kernel thread is maintained within the kernel's address space • Kernel threadsthe fundamental entities that can be scheduled and dispatched to run on one of the system processors

  16. Process, Threads, and in the Solaris

  17. CPU Code Data Java中的线程 • Java 中线程被认为是一个CPU、程序代码、和数据的封装体 一个虚拟的CPU, 该CPU执行的代码: 代码与数据是相互独立的,代 码可以与其它线程共享。 代码所操作的数据:数据也可以 被多个线程共享。

  18. Java 线程的作用 • Java 线程 • 从语言级提供对多线程的支持 (包括用户线程和系统线程),并提供对共享数据管理功能和同步机制。 • 应用Java多线程机制可以有效处理动画程序、游戏程序和大型Web服务程序等。 • Java线程分为: • 主线程:通过JVM启动的第一个线程。 • 子线程(Thread) • 通过Java应用程序创建的线程。 • 守护线程(Daemon) • 一种用于监视其他线程的服务线程。

  19. 线程的生命周期 • 一个线程从创建→工作→死亡的过程称为线程的生命周期 • 一个线程周期有四种状态: ⑴ 新建状态 ⑵ 可运行状态 ⑶ 中断(阻塞)状态 ⑷ 死亡状态

  20. 创建状态 • 新建状态 • 指创建了一个线程还没有启动它(此时它已经有了相应的内存空间和其他资源)。 • 创建新的线程通过Thread类及其子类说明 线程类名 线程对象名 = new 线程类名(线程名);

  21. 可运行状态 • 就绪状态 • 指具备了运行条件,进入可运行队列排队等待CPU服务,一旦轮到它来享用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期。 • 当新建状态的线程调用了start( )方法进入就绪状态。 • 运行状态 • 当就绪状态的线程获得CPU处理器资源时,便进入工作运行状态。 • 每一个Thread类及其子类的对象都有一个重要的run()方法,当线程对象被调度执行时,它将自动调用该对象的run()方法,从方法体的第一句开始顺序执行,直到完成这个线程操作的所有功能。

  22. 中断状态 • 当正在运行的线程遇到某些特殊情况(如:延迟、挂起、等待和I/O设备等原因)时, 该线程将让出CPU并暂时中止自己的执行,进入中断(阻塞)状态。 • 线程进入中断状态,它不再参加排队,只有当引起阻塞的原因被消除后,线程又可以转入就绪状态,重新进入排队等待CPU资源,以便从原来终止处开始继续运行。 • 恢复运行态通常有三种途径: • 自动恢复(如:sleep、I/O操作) • 用resume()方法恢复 • 用notify()或notifyAll()方法恢复

  23. 死亡状态 • 有两种情况使一个线程终止,进入死亡状态 • 自然撤消(线程完成了全部工作,正常退出) • 被提前强制性地终止: • 调用stop()方法 • 调用interrupt()方法 • 注意:线程进入死亡状态,就不具有继续运行的能力,也不能再转到其他状态

  24. 阻塞 新建 死亡 run()出口 Interrupt() stop() stop( ) 可运行 线程的生命周期图 yield( ) 线程的状态 wait() suspend() sleep() I/O操作 new Thread( ) start( ) resume() notify() stop( )

  25. 线程调度与优先级 • Java 线程调度器 • 监视控制就绪状态的所有线程 • 线程调度策略(采用抢占式 ) • 优先级高的线程比优先级低的线程先执行; 优先级相同的情况下,按“先到先服务”原则 。 • 线程的优先级用常数表示。 • 每个线程根据继承特性自动获得一个线程的优先级,也可在程序中设置。 • 对于任务较紧急的重要线程,可安排优先级较高的;相反则较低。

  26. 设置/获取线程优先级方法 • getPriority()方法 • 获得线程的优先级 • setPriority(int 优先级) • 改变线程的优先级方法。创建时,继承父进程的优先级 • 优先级 • 数值越大优先级越高(范围 1-10,缺省是5 ) • 最低:Thread.MIN-PRIORITY • 最高: Thread.MAX-PRIORITY • 标准:Thread.NORM-PRIORITY • 举例 setPriority(Thread.MIN-PRIORITY) • 例:PriorityTest.java

  27. Java编程中实现多线程的办法 • Java的线程实现主要通过Java.lang包中的Thread类和Runnable接口。 • 两种方式 • 通过继承Thread类定义一个线程类,对线程的操作要求可在该类的run( )方法中定义,再用此类创建对象来实现一个线程。 • 通过一个类去继承接口Runnable来实现线程的创建,这个类必须提供Runnable接口中的run()方法的实现。

  28. Thread类 • Thread类 • 专门用来创建线程和对线程进行操作的类。 • Thread类的构造方法 • public Thread( ); • 创建一个线程 • public Thread(String m); • 创建一个以m命名的线程 • public Thread(Runnable target); • 创建线程,参数target称为被创建线程的目标对象。 • public Thread(ThreadGroup g, String m); • 创建一个以m命名的线程类,该类属于指定的线程组g • 例:ThreadGroupTest.java

  29. 线程的启动、操作和延迟方法 • start(); • 启动,由新建态到就绪态 • run(); • 实现线程行为(操作)的方法 • sleep(延迟时间); sleep(int millsecond); 或 sleep(int millsecond,int nanosecond); • 令线程在指定时间段内放弃对CPU控制,使同优先级其他线程有机会被执行,时间到重新排队 • 产生例外Interrupted Exception • 用try块调用sleep(),用catch块处理例外 • static方法,不可重载

  30. 创建线程 (Thread类) • Thread类创建线程的例子 import java.lang.* class MyThread extends Thread{ public void run() { //子类run方法覆盖父类run …… } } • 用new 使线程进入创建状态。 MyThread t = new MyThread(); t.start(); //用start()方法使线程进入可运行状态

  31. 获取线程名字和判断状态方法 • currentThread() • 判断当前正在占有CPU的那个线程。 • 举例:MainThread.java • getName() • 获取线程的名字 • setName() • 设置线程的名字 • isAlive() • 返回boolean, 表明是否还活跃

  32. 停止线程执行方法 • stop()与destroy() • 强制线程生命期结束 • stop()还完成一些清理工作,并抛出例外 • suspend() • 挂起线程,处于不可运行(阻塞)状态 • resume() • 恢复挂起的线程,重新进入就绪队列排队 • yield()//对正在执行的线程 • 若就绪队列中有与当前线程同优先级的排队线程, 则当前线程让出CPU控制权,移到队尾 • 若队列中没有同优先级的线程,忽略此方法

  33. Thread类创建方法举例 • 例 • 在main主线程中创建了两个新的线程lefthand和righthand。当lefthand调用start()开始运行时,类Lefthand中的run()将自动被执行。 … • 分析程序的输出结果。 • Left线程先执行,这时run方法输出 “伸出左手”后,left主动“休息500毫秒,让出CPU。这时正在等待CUP的right线程获得运行资源,输出 “换右手”,right线程主动让出CPU300 毫秒后又来排队等待CPU服务,过了300毫秒后,发现left线程还没有“醒来”,因此有轮到right。又输出“换右手”

  34. public class ExampleHand{ static Lefthand left; static Righthand right; public static void main(String args[ ]){ left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } }

  35. class Lefthandextends Thread { public void run(){ for(int i=0; i<=5; i++){ System.out.println("伸出左手。"); try{sleep(500);} catch(InterruptedExceptione){} } } } • 例:BidThread.java

  36. Runnable接口 • 方法run() • 线程的所有活动都是通过线程体的方法run来实现 • run 方法体定义该线程的具体的执行内容。 • 当一个线程启动后,Java系统就自动地调用run()方法。 • 通常run()方法的工作是一种循环。 • 一个实现了Runnable接口的类实际上定义了一个主线程之外的新线程的操作,而定义新线程的操作和执行流程,是实现多线程应用的最主要和最基本的工作

  37. 创建线程 (实现Runnable接口) • 接口Runnable创建线程的例子 public class xyz implement Runnable { //定义一个接口连接 int i; public void run( ) { //定义一个抽象方法run()的实现 while (true) { System.out.println(“Hello” + i++); } } } • 先建一个对象,再创建一个线程 Runnable r = new xyz( ); Thread t = new Thread( r ); //这种方法比较灵活, *对于一个类既需要继承父类又要由此创建一个线程时,可用接口解决 • 例:BidRun.java Auction.java

  38. 采用间接创建线程的原因 • 第一个理由是我们并不改变线程本身的性质,仅覆盖run方法,并没有增加新的功能,因此将Thread扩展子类并不恰当,这不太符合类扩展规范。 • 第二个理由是:如果实现Runnable接口,它可能使我们所设计的类扩展其它类型而变得更为有用。

  39. Daemon线程 • Daemon线程 • 称为系统守护线程,一种专门为其他线程提供服务的线程(如:内存垃圾收集) • 通常在一个较低的优先级上运行 • 守护线程的特点:无限循环运行 • Thread类提供的方法 setDaemon( true );//将线程设置为守护线程 isDaemon( );//检测是否守护线程

  40. Daemon线程举例 Daemon 线程的框架是: class DaemonThread extend Thread { DaemonThread( ) { setDaemon( true ); start( ); } public void run( ) { while ( true ) {... // 等待服务请求和处理 } } } 例:DaemonTest.java

  41. 与线程所处状态有关的方法举例 • join方法:等待指定的线程运行结束. • 例子:JoinTest.java • yield方法:让当前线程主动放弃对CPU的占用. • 例子:YieldTest.java • interrupt方法的例子: InterruptTest.java

  42. 多线程并发执行中的问题 • 多个线程相对执行的顺序是不确定的。 • 线程执行顺序的不确定性会产生执行结果的不确定性。 • 在多线程对共享数据 操作时常常会产生这种不确定性。 • 举例: ThreadSharedData.java

  43. 银行业务中共享资源冲突示意 举例: AccountSimulator.java

  44. 线程间的同步 • 线程同步 • 解决多线程争夺同一变量 (共享资源) • 线程同步技术,用于协调多线程中对某个方法或者某个代码的执行秩序 • 避免多线程在并发运行时对共享数据处理过程中,发生的数据不一致问题 • 如何进行多线程中的同步控制 • 同步控制原理、编程方法和处理原则 • wait()、notify()和notifyAII()方法的使用

  45. 同步控制原理 • 管程(monitor) • 实现加锁,解决多线程中争夺共享资源 • 管程作用 • 控制资源使用,一个资源在同一时刻只能供一个线程使用 • 当资源未被占用,线程可以进入(这个资源的)管程,从而得到该资源的使用权;当线程执行完毕,便退出管程 • 如果一个线程已进入某资源的管程,其它的线程必须等待 • 管程与Java对象 • 每一个Java对象都与一个管程联系 • 在同一类中的所有静态同步方法都使用同类的管程 • 所有实例同步方法使用同一个实例对象管程

  46. Java编程中的同步控制 • 关键字synchronized定义临界区 • 锁住共享资源 • 在Java中可以是某个方法或者某个代码块 • 同步的协调方法 • 方法 wait( ) notify( ) notifyAll( )

  47. 同步控制举例 • 例有两个线程:会计和出纳,他俩共同拥有一个账本。 • 他俩都可以使用存取方法对账本进行访问,会计使用存取方法时,向账本上写入存钱记录;出纳使用存取方法时, 向账本写入取钱记录。 • 因此,当会计正在使用账本时,出纳被禁止使用,反之也是这样。比如, 会计每次使用账本时,在账本上存入90元钱,但在存入这笔钱时,每写入30元,就喝口茶,那么他喝茶休息时(注意,这时存钱这件事还没结束,即会计还没有使用完存取方法),出纳仍不能使用账本。从周一到周三会计和出纳都要使用账本,我们要保证其中一人使用账本时,另一个人必须等待。

  48. import java.applet.*; import java.awt.*; import java.awt.event.*; public class Example19_7 extends Applet implements Runnable{ int money=100; TextArea text1,text2; Thread 会计,出纳; public void init(){ 会计=new Thread(this); 出纳=new Thread(this); text1=new TextArea(20,8); text2=new TextArea(20,8); add(text1);add(text2); } public void start(){ 会计.start(); 出纳.start(); //线程开始。 }

  49. //存取方法 public synchronized void 存取(int number) { if(Thread.currentThread() == 会计){ for(int i=1; i<=3; i++) { //会计使用存取方法存入90元,存入30元,稍歇一下, money=money+number; //这时出纳仍不能使用存取方法, try{Thread.sleep(1000);} //因为会计还没使用完存取方法。 catch(InterruptedExceptione){} text1.append("\n"+money);}} if(Thread.currentThread() == 出纳){ for(int i=1; i<=2; i++) { //出纳使用存取方法取出30元,取出15元,稍歇一下, money=money-number/2; //这时会计仍不能使用存取方法, try{Thread.sleep(1000);} //因为出纳还没使用完存取方法。 catch(InterruptedExceptione){} text2.append("\n"+money); } } }

  50. public void run(){ if(Thread.currentThread()==会计||Thread.currentThread()==出纳){ for(int i=1;i<=3;i++) //从周一到周三会计和出纳都要使用账本。 { 存取(30); } } } } <HTML> <APPLET CODE="Example.class" WIDTH=400 HEIGHT=200> </APPLET> </HTML>

More Related