多线程程序设计模板课件_第1页
多线程程序设计模板课件_第2页
多线程程序设计模板课件_第3页
多线程程序设计模板课件_第4页
多线程程序设计模板课件_第5页
已阅读5页,还剩42页未读 继续免费阅读

下载本文档

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

文档简介

1、第11讲 多线程程序设计11 线程的概念 2 线程的创建3 线程的生命周期及调度 4 线程互斥 5 线程同步 6 线程通信 7 线程死锁 本讲内容21. 线程的概念 程序(Program):程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,程序是静态的代码。进程(Process):进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。线程(Thread):线程是一个比进程更小的执行单位。一个进程在其执行过程中可以产生多个线程,形成多条执行线路。3线程与进程的区别一个进程中可以包含多个线程。一个线程是一个程序内部的顺序控制流。主要区别如下:1. 进程:每个进程都有独

2、立的代码和数据空间(进程上下文) ,进程切换的开销大。 2. 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。 3. 多进程:在操作系统中,能同时运行多个任务程序。 4. 多线程:在同一应用程序中,有多个顺序流同时执行。1.线程的概念4使用多线程进行程序设计具有如下优点:多线程编程简单,效率高(能直接共享数据和资源,多进程不能)适合于开发服务程序(如Web服务,聊天服务等)适合于开发有多种交互接口的程序(如聊天程序的客户端,网络下载工具)减轻编写交互频繁、涉及面多的程序的困难(如监听网络端口)程序的吞吐量会得到改善(同时监听多种设备

3、,如网络端口、串口、并口以及其他外设)有多个处理器的系统,可以并发运行不同的线程(否则,任何时刻只有一个线程在运行)1. 线程的概念5Hotjava浏览器就是一个多线程应用的实例。当下载一个应用程序或图片时,可以同时进行其他任务,例如播放动画或声音的应用程序、打印某些内容、进行排序或者其他工作。聊天室(多人同时聊)Web服务器 ,如: Tomcat、Resin、WebSphere数据库系统,如:MSSQLServer、Oracle线程举例:6线程所执行的代码通过方法run()(包含在一个特定的对象中, 称为线程体)来完成。通常,run( )方法是一个循环,例如:一个播放动画的线程要循环显示一系

4、列图片。run( )方法有时会执行一个时间较长的操作,例如:下载并播放一个JPEG格式的电影。创建线程并初始化后,Java的运行时系统自动调用run( )方法,实现线程所要完成的功能。创建线程有两种主要的方法:一种是继承Thread类;另一种是实现接口Runnable。2.线程的创建 7该方法比较简单,主要是通过继承java.lang.Thread类,并覆盖Thread类的run()方法来完成线程的创建。Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类继承的新类。Thread类中有两个最重要的方法run()和start(

5、)。run()是线程的主体,Thread的子类应该覆盖该方法;start()启动该线程,JVM会调用该线程对象的run()方法 2.1 继承Thread类创建线程 8Thread的构造方法及主要方法说明Thread()创建一个没有名称的线程对象Thread(Stringname)创建一个名称为name的线程对象Thread(Runnabletarget)创建一个以target为参数的线程对象Thread(Runnabletarget, Stringname)创建一个target为参数,名称为name的线程对象Thread(ThreadGroupgroup, Stringname)创建一个归属于

6、group线程组,名称为name的线程Thread(ThreadGroupgroup, Runnabletarget)创建一个归属于group线程组,target为参数的线程public static int activeCount()返回当前线程的线程组中活动线程的数目public static Thread currentThread()返回当前的线程对象public long getId()返回该线程的ID,每个线程都有一个唯一的IDpublic final String getName()返回该线程的名称public final int getPriority()返回该线程的优先级pu

7、blic Thread.State getState()返回该线程的状态,以监视系统的运行状态public final ThreadGroup getThreadGroup()返回当前的线程组2.1 继承Thread类创建线程 9public void interrupt()中断线程public static boolean interrupted()判断当前线程是否已经中断,若中断则清除中断状态public final boolean isAlive()判断线程是否处于活动状态,若线程已经启动但尚未终止称为活动状态public final boolean isDaemon()判断线程是否为守

8、护线程public boolean isInterrupted()判断该线程是否已经中断,且线程的状态不受此方法影响public final void join()等待该线程终止public final void resume()重新开始挂起的线程,已过时public void run()线程的主体,Thread的子类应该覆盖该方法;实现Runnable接口的对象应该实现该方法 public final void setDaemon(booleanon)设置该线程是否为守护线程public final void setName(Stringname)设置线程的名称public final vo

9、id setPriority(intnewPriority)设置线程的优先级public static void sleep(longmillis)使该线程休眠millis毫秒public void start()启动该线程,JVM会调用该线程对象的run()方法public final void stop()强制停止该线程,已过时public final void suspend()挂起该线程,已过时public static void yield()暂停当前线程,让处于活动状态的线程重新抢占控制权10该方法通过生成实现java.lang.Runnable接口的类。该接口只定义了一个方法ru

10、n(),所以必须在新类中实现它。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造方法 public Thread(Runnable target); 来实现。2.1 实现Runnable接口创建线程 113.1 线程生命周期 3.2 线程调度和优先级 3. 线程的生命周期及调度 12线程是动态的,具有一定的生命周期,分别经历从创建、执行、阻塞直到消亡的过程。在每个线程类中都定义了用于完成实际功能的run方法,这个run方法称为线程体(Thread Body)。按照线程体在计算机系统内存中的状态不同,可以将线程分为创建

11、(new)、就绪(runnable)、运行(running)、阻塞(blocked)和死亡(dead)5个状态。 3.1 线程生命周期 13线程状态创建状态new Thread(.)就绪状态start()阻塞状态运行状态sleep()suspend()wait()I/O获得CPUrun() 结束stop()yield()消亡sleep时间到resume()notify()或notifyAll()I/O完成14创建状态(new Thread) 当创建了一个新的线程时( myThread thd = new myThread(); ),它就处于创建状态,此时它仅仅是一个空的线程对象,系统不为它分配

12、资源。处于这种状态时只能启动或终止该线程,调用除这两种以外的其它线程状态相关的方法都会失败并且会引起非法状态例外IllegalThreadStateException(对于其它状态,若所调用的方法与状态不符,都会引起非法状态例外)。15 就绪状态(Runnable) 当线程处于新建状态时,可以调用start()方法( thd.start(); )来启动它,产生运行这个线程所需的系统资源,安排其运行,并调用线程体run()方法,这样就使得该线程处于可运行( Runnable )状态。 需要注意的是这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行(Ready)。由于很多

13、计算机都是单处理器的,所以要在同一时刻运行所有的处于可运行状态的线程是不可能的,Java运行系统必须实现调度来保证这些线程共享处理器。但是在大多数情况下,可运行状态也就是运行中。当一个线程正在运行时,它是可运行的,并且也是当前正运行的线程。16 阻塞状态(Blocked) 线程处于运行状态时,当下面四种情况发生,线程就进入不可运行(阻塞)状态: 调用了sleep()方法; 调用了suspend()方法; 为等候一个条件变量,线程调用wait()方法; 输入输出流中发生线程阻塞。Thread myThread = new MyThreadClass();myThread.start();try

14、myThread.sleep(10000); catch (InterruptedException e) 17 对于这四种使得线程处于不可运行状态的情况,都有特定的方法使线程返回可运行状态: 如果线程处于睡眠状态中,sleep()方法中的参数为休息时间,当这个时间过去后,线程即为可运行的; 如果一个线程被挂起,须调用resume()方法来返回; 如果线程在等待条件变量,那么要停止等待的话,需要该条件变量所在的对象调用notify()或notifyAll()方法; 如果在I/O流中发生线程阻塞,则特定的I/O指令将结束这种不可运行状态。 需要注意的是每种方法都仅仅对相应的情况才有作用,例如当一

15、个线程睡眠并且睡眠时间还没有结束时,调用resume()方法是无效的,并且还会引起非法状态例外。18 死亡状态(Dead) 线程的终止一般可通过两种方法实现:自然撤消或是被停止。自然撤消是指从线程的run()方法正常退出;而调用线程的实例方法stop()则可以强制停止当前线程。/线程自然撤销public void run() int i = 0; while (i = 100) try sleep(delay); acount.balance = acount.balance - 100; System.out.println(withdraw 100 successful!); catch(

16、InterruptedException e) else System.out.println(withdraw failed!); 4.1问题的提出28public static void main(String args) Acount acount = new Acount(100); AcountThread acountThread1 = new AcountThread(acount,1000); AcountThread acountThread2 = new AcountThread(acount,0); acountThread1.start(); acountThread2

17、.start(); 4.1问题的提出29该结果非常奇怪,因为尽管账面上只有100元,但是两个取钱线程都取得了100元钱,也就是总共得到了200元钱。出错的原因在哪里呢?由于线程1在判断满足取钱的条件后,被线程2打断,还没有来得及修改余额。因此线程2也满足取钱的条件,并完成了取钱动作。从而使共享数据balance的完整性被破坏。 4.1问题的提出参见AcountThread.java 运行结果如下:Totle Money: 100.0withdraw 100 successful!withdraw 100 successful!30上面的问题,并不是新问题,其实在并发程序设计中已经被研究并得到了

18、解决。我们首先回忆两个概念。在并发程序设计中,对多线程共享的资源或数据成为临界资源,而把每个线(进)程中访问临界资源的那一段代码段成为临界代码段。通过为临界代码段设置信号灯,就可以保证资源的完整性,从而安全地访问共享资源 4.2 互斥对象 31为了实现这种机制,Java语言提供以下两方面的支持:1 为每个对象设置了一个“互斥锁”标记。该标记保证在每一个时刻,只能有一个线程拥有该互斥锁,其它线程如果需要获得该互斥锁,必须等待当前拥有该锁的线程将其释放。该对象成为互斥对象。2 为了配合使用对象的互斥锁,Java语言提供了保留字synchronized, 其基本用法如下:加锁1 (临界区-方法) s

19、ynchronized 方法名 /进入该方法时加锁加锁2(临界区-代码块) 方法名 . synchronized(this) /进入该代码段时加锁 . 4.2 互斥对象修改后的程序AccountThread2.java 32class Semaphorepublic class AcountThread2 extends Thread Acount acount; int delay; Semaphore semaphore; public AcountThread2(Acount acount,int delay,Semaphore semaphore) this.acount =acoun

20、t; this.delay = delay; this.semaphore = semaphore; AccountThread2.java33public void run() synchronized (semaphore) if (acount.balance = 100) try sleep(delay); acount.balance = acount.balance - 100; System.out.println(withdraw 100 successful!); catch (InterruptedException e) else System.out.println(w

21、ithdraw failed!); 34public static void main(String args) Acount acount = new Acount(100); Semaphore semaphore = new Semaphore(); AcountThread2 acountThread1 = new AcountThread2(acount,1000,semaphore); AcountThread2 acountThread2 = new AcountThread2(acount,0,semaphore); acountThread1.start(); acountT

22、hread2.start(); 运行结果如下:Totle Money: 100.0withdraw 100 successful!withdraw failed!35 除了要处理多线程间共享数据操作的同步问题之外,在进行多线程程序设计时,还会遇到另一类问题,这就是如何控制相互交互的线程之间的运行进度,即多线程的同步。 典型的模型:生产者消费者问题5. 线程同步生产者消费者共享对象putget 若共享对象中只能存放一个数据,可能出现以下问题: 生产者比消费者快时,消费者会漏掉一些数据没有取到; 消费者比生产者快时,消费者取相同的数据。36 为了解决所出现的问题,在Java语言中可以用wait (

23、) 和notify()/notifyAll()方法(在java.lang.Object类中定义)来协调线程间的运行进度(读取)关系。 wait()方法的作用是让当前线程释放其所持有的“对象互斥锁”,进入wait队列(等待队列);而notify()/notifyAll()方法的作用是唤醒一个或所有正在等待队列中等待的线程,并将它(们)移入等待同一个“对象互斥锁”的队列。 需要指出的是: notify()/notifyAll()方法和wait ()方法都只能在被声明为synchronized的方法或代码段中调用。5. 线程同步37线程同步举例:Wait_Notify 程序CubbyHole.jav

24、a创建用户的线程子类Producer:产生数据(存数据);Consumer:消费数据(取数据)CubbyHole类,共享数据区,同步方法put(int value)方法int get()方法主类中创建共享数据对象,并启动两线程38class CubbyHole private int seq; / 共享数据 private boolean available = false; / 条件标志变量 public synchronized int get() /取数据的同步方法get()while( available= =false ) trywait( ); /条件不符合,则wait catch

25、( InterruptedException e ) available = false; /修改条件notify( ); /通知唤醒其他等待的线程return seq; /返回要取出的数值 /end of get()39/存放数据的同步方法put() public synchronized void put(int value)while( available= =true ) trywait( ); /条件不符合,则wait catch( InterruptedException e ) seq = value; /把共享变量修改为要放置的数据available = true; /修改条件

26、notify( ); /通知唤醒其他等待的线程 /end of put() /end of class CubbyHole40class Producer extends Thread /生产者线程类 private CubbyHole cubbyhole; private int number; public Producer(CubbyHole c, int number) cubbyhole = c; this.number = number; public void run() /定义run()方法 for (int i = 0; i 10; i+) /共产生10个 cubbyhole

27、.put(i); System.out.println(Producer #+this.number + put: + i); try sleep(int)(Math.random() * 100); catch (InterruptedException e) /end of class Producer41class Consumer extends Thread /消费者线程类 private CubbyHole cubbyhole; private int number; public Consumer(CubbyHole c, int number) cubbyhole = c; this.number = number; public void run() /定义run()方法 int value = 0; for (int i = 0; i 10; i+) /消费10个 value = cubbyhole.get(); System.out.println(Consumer #+this.number+ got: +value); /end of cla

温馨提示

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

评论

0/150

提交评论