1 / 48

第九章 多线程程序设计

第九章 多线程程序设计. §9.1 线程的概念 §9.2 Java 线程的创建 §9.3 线程状态和线程控制 §9.4 线程优先级和线程的调度 §9.5 线程同步 §9.6 线程的死锁. §9.1 线程的概念. 进程:一个执行中的程序,有自己独立的内存空间等系统资源。 线程:程序中单个顺序的流控制,单个程序中多个线程共享系统资源。 多线程编程:将任务分成多个并发的子任务。 Java 支持多线程。. §9.2 Java 线程的创建. 继承 Thread 类的方法 实现 Runnable 接口的方法

Download Presentation

第九章 多线程程序设计

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. 第九章 多线程程序设计 §9.1 线程的概念 §9.2 Java 线程的创建 §9.3 线程状态和线程控制 §9.4 线程优先级和线程的调度 §9.5 线程同步 §9.6 线程的死锁

  2. §9.1 线程的概念 • 进程:一个执行中的程序,有自己独立的内存空间等系统资源。 • 线程:程序中单个顺序的流控制,单个程序中多个线程共享系统资源。 • 多线程编程:将任务分成多个并发的子任务。 • Java支持多线程。

  3. §9.2 Java 线程的创建 • 继承Thread类的方法 • 实现Runnable接口的方法 线程体 有run方法(一般是一个循环,需 要以线程方式运行的代码)

  4. 1 继承Thread类的方法 • 通过实现Thread类的子类、并置换其中的run()方法定义线程体,然后创建该子类的对象创建线程。如: public class Counter extends Thread{ … public void run(){ //定义线程的行为 } … } • 启动线程 Counter threadCounter = new Counter(); threadCounter.start();

  5. 2 实现Runnable接口的方法 • 通过在类中实现Runnable接口,并在该类中提供run()方法的实现。如: public class Counter implements Runnable{ public void run(){ //定义线程的行为 } } • 启动线程 • Counter C1 = new Counter(); new Thread(C1).start(); • Counter C1 = new Counter(); Thread t1 = new Thread(C1); t1.start();

  6. 3 Thread类构造器和Threadgroup类 • Thread 类构造器: Thread() Thread(Runnable) Thread(Threadgroup,Runnable) Thread(String) Thread(Threadgroup,String) Thread(Runnable,String) Thread(Threadgroup,Runnable,String) • Runnable:类的实例,调用Run()方法的对象 • Threadgroup:新创建的线程所属的线程组 • String:表示新线程的名字

  7. 3 Thread类构造器和Threadgroup类 • Threadgroup 类(线程组):所有的线程一定属于某个线程组。属于Java.lang包。用于进行统一管理。 • 显式:ThreadGroup myThreadGroup = new ThreadGroup(“my group of threads”) • 隐式:新创建的线程自动的属于创建该线程的线程所在的线程组。

  8. 例 线程概念例子 class PrintThread extends Thread { private int sleepTime; public PrintThread( String name ) { super( name ); sleepTime = (int) ( Math.random() * 5000 ); System.out.println( "Name: " + getName() + "; sleep: " + sleepTime ); }

  9. 例 线程概念例子 public void run() { try { System.out.println( getName() + " going to sleep" ); Thread.sleep( sleepTime ); }catch ( InterruptedException exception ) { System.err.println( exception.toString() );} System.out.println( getName() + " done sleeping" ); } }

  10. 例 线程概念例子 public class PrintThreadTest { public static void main( String args[] ) { PrintThread thread1 = new PrintThread( "thread1" ); PrintThread thread2 = new PrintThread( "thread2" ); PrintThread thread3 = new PrintThread( "thread3" ); PrintThread thread4 = new PrintThread( "thread4" ); System.out.println( "\nStarting threads" ); thread1.start(); thread2.start(); thread3.start(); thread4.start(); System.out.println( "Threads started\n" ); } }

  11. §9.3 线程状态和线程控制 Runnable状态 Running状态 Not Runnable状态 Dead状态 New Thread状态 改变 对线程进行各种控制 Thread类中的方法实现 • 类Thread的常用方法 • 控制和状态的关系 • 例子 线程状态

  12. 1 类Thread的常用方法

  13. 1 类Thread的常用方法

  14. 1 类Thread的常用方法

  15. 2 控制和状态的关系 Start() 获得互斥使用权 创建 可运行 线程结束 或时间到 或interupt() 其它 阻塞 Yield() 调度时间到 Sleep() Join() 互斥阻塞 运行中 Synchronized Notify() Interrupt() Wait() 运行结束 等待阻塞 不可运行 死亡

  16. 3 例子—时钟 import java.awt.Graphics; import java.util.*; import java.applet.Applet ; public class Clock extends Applet implements Runnable{ Thread clockThread; public void start(){ if(clockThread == null){ clockThread = new Thread(this,"Clock"); clockThread.start();}} public void run(){ while(clockThread!=null){ repaint();

  17. 3 例子—时钟 try{ clockThread.sleep(1000); }catch(InterruptedException e){ } } } public void paint(Graphics g){ Calendar now = new GregorianCalendar(); Date trialTime = new Date(); now.setTime(trialTime); g.drawString(now.get(Calendar.HOUR_OF_DAY)+":" +now.get(Calendar.MINUTE)+":"+now.get(Calendar.SECOND),5,10); } public void stop(){ clockThread = null; } }

  18. 3 例子—时钟 <html> <applet code = "Clock.class" width = 50 height = 40> </applet> </html>

  19. §9.4 线程优先级和线程的调度 • 线程的调度取决于线程的优先级。采用先占式调度,先占调度有分为: • 独占方式 • 分时方式 • 线程的优先级可以用setPriority()显式进行设置。getPriority()获得优先级。 • 线程优先级用整数表示。从1到10,Thread.MIN_PRIORITY(1), Thread.MAX_PRIORITY(10), Thread.NORM_PRIORITY(5) • 例

  20. §9.4 线程优先级和线程的调度—画线 import java.applet.Applet; import java.awt.Color; public class RaceApplet extends Applet implements Runnable{ final static int NUMRUNNERS = 2; final static int SPACING = 20; Runner runners[] = new Runner[NUMRUNNERS]; Thread updateThread = new Thread(this,"control"); public void init(){for(int i=0;i<NUMRUNNERS;i++){ runners[i] = new Runner(); runners[i].setPriority(i+1);} } public boolean mouseDown(java.awt.Event evt,int x,int y){

  21. §9.4 线程优先级和线程的调度 if(!updateThread.isAlive()) updateThread.start(); for(int i=0;i<NUMRUNNERS;i++){ if(!runners[i].isAlive()) runners[i].start();} return true; } public void paint(java.awt.Graphics g){ g.setColor(Color.lightGray); g.fillRect(0,0,400,500); g.setColor(Color.black); for(int i=0;i<NUMRUNNERS;i++){ int pri = runners[i].getPriority(); g.drawString(new Integer(pri).toString(),0,(i+1)*SPACING); } update(g); }

  22. §9.4 线程优先级和线程的调度 public void update(java.awt.Graphics g ){ for(int i=0;i<NUMRUNNERS;i++) g.drawLine(SPACING,(i+1)*SPACING,SPACING+(runners[i].tick)/1000 ,(i+1)*SPACING); }public void run(){ while(updateThread!=null){ repaint(); try{ updateThread.sleep(100); }catch(InterruptedException e){ } } }

  23. §9.4 线程优先级和线程的调度 public void stop(){ for(int i=0;i<NUMRUNNERS;i++) { if(runners[i].isAlive()){runners[i] = null;} if(updateThread.isAlive()){ updateThread = null;} } } Class Runner extends Thread{ public int tick = 1; public void run(){ while(tick<400000) tick++; } }

  24. §9.5 线程同步-- 数据的完整性 线程2 监视器 线程1 withdrawal() • 因多线程并发而引起执行顺序的不确定性,执行的不确定性会产生执行结果的不确定性。在多个线程需要共享数据时通常会产生这种不确定性。对共享对象的访问必须同步,叫做条件变量. • Java语言允许通过监视器(有的参考书称其为管程)使用条件变量实现线程同步. • 监视器阻止两个线程同时访问同一个条件变量.它如同锁一样作用在数据上. • 线程1进入withdrawal方法时,获得监视器(加锁);当线程1的方法执行完毕返回时,释放监视器(开锁),线程2的withdrawal方能进入.

  25. §9.5 线程同步-- 数据的完整性 线程1 线程2 监 视 器 read write • 用synchronized来标识的区域或方法即为监视器监视的部分。 • 一个类或一个对象有一个监视器,如果一个程序内有两个方法使用synchronized标志,则他们在一个监视器管理之下. • 一般情况下,只在方法的层次上使用关键区

  26. §9.5 线程同步-- 数据的完整性 此处给出的例子演示两个线程在同步限制下工作的情况. class Account { static int balance=1000; //为什么用static? static int expense=0; public synchronized void withdrawl(int amount) { if (amount<=balance) { balance-=amount; expense+=amount;} else { System.out.println(“bounced: “+amount);} } }

  27. §9.5 线程同步--等待同步数据 . 生产者 消费者 共享对象 write . read 可能出现的问题: • 生产者比消费者快时,消费者会漏掉一些数据没有取到 • 消费者比生产者快时,消费者取相同的数据. • notify()和wait ()方法用来协调读取的关系. • notify()和wait ()都只能从同步方法中的调用.

  28. §9.5 线程同步--等待同步数据 • notify的作用是唤醒正在等待同一个监视器的线程. • wait的作用是让当前线程等待 • read()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程. • write()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程.

  29. 例 生产者/消费者例子(有同步) // HoldIntegerSynchronized.java public class HoldIntegerSynchronized { private int sharedInt = -1; private boolean writeable = true; // condition variable public synchronized void setSharedInt( int val ) { while ( !writeable ) { // not the producer's turn try { wait(); } catch ( InterruptedException e ) { e.printStackTrace(); } } System.err.println( Thread.currentThread().getName() + " setting sharedInt to " + val );

  30. sharedInt = val; writeable = false; notify(); // tell a waiting thread to become ready } public synchronized int getSharedInt() { while ( writeable ) { // not the consumer's turn try { wait(); } catch ( InterruptedException e ) { e.printStackTrace(); } } writeable = true; notify(); // tell a waiting thread to become ready

  31. System.err.println( Thread.currentThread().getName() + " retrieving sharedInt value " + sharedInt ); return sharedInt; }} // ProduceInteger.java public class ProduceInteger extends Thread { private HoldIntegerSynchronized pHold; public ProduceInteger( HoldIntegerSynchronized h ) { super( "ProduceInteger" ); pHold = h; } public void run() { for ( int count = 1; count <= 10; count++ ) { pHold.setSharedInt( count ); }

  32. System.err.println( getName() + " finished producing values" + "\nTerminating " + getName() ); } } // ConsumeInteger.java public class ConsumeInteger extends Thread { private HoldIntegerSynchronized cHold; public ConsumeInteger( HoldIntegerSynchronized h ) { super( "ConsumeInteger" ); cHold = h; } public void run() { int val, sum = 0; do { val = cHold.getSharedInt(); sum += val;

  33. } while ( val != 10 ); System.err.println(getName() + " retrieved values totaling: " + sum + "\nTerminating " + getName() ); } } // SharedCell.java public class SharedCell { public static void main( String args[] ) { HoldIntegerSynchronized h = new HoldIntegerSynchronized(); ProduceInteger p = new ProduceInteger( h ); ConsumeInteger c = new ConsumeInteger( h ); p.start(); c.start(); } }

  34. §9.6 线程的死锁 把“pen”给我,我 才能给你“note” 线程2 线程1 pen note 把“note”给我,我 才能给你“pen” • 死锁:指两个或多个线程无止境地相互等待的过程。错误的同步往往会引起死锁。 • 程序员认真设计避免死锁。如果你持有一个锁并试图获取另一个锁时,就有死锁的危险. • 解决死锁问题的方法:对共享资源访问的顺序,即给条件变量施加排序。

  35. §9.7 多线程问题---线程间的通信 PipedOutputStream PipedInputStream 线程2 线程1 输出流outStream 输入流inStream 1. 线程间的通信可以用管道流 创建管道流: PipedInputStream pis=new PipedInputStream(); PipedOutputStream pos=new PipedOutputStream(pis); 或: PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(pos);

  36. §9.7 多线程问题---线程间的通信 printStream DataInputStream • 管道流不能直 接读写 PrintStream p = new PrintStream( pos ); p.println(“hello”); DataInputStream d=new DataInputStream(pis); d.readLine(); • 管道流可以连接两个线程间的通信 • 将一个写线程的输出通过管道流定义为读线程的输入.

  37. §9.7多线程问题--线程间的通信 主类Pipethread 作为参数传给Writer Writer( outStream ) 辅类Writer 线 程 类 管 道 流 辅类 Reader 线 程 类 将数据写 到输出流 输入流 从流中读数据

  38. 7.4 多线程问题--线程间的通信 public class Pipethread { public static void main(String args[]) { Pipethread thisPipe = new Pipethread(); thisPipe.process(); } public void process() { PipedInputStream inStream; PipedOutputStream outStream; PrintStream printOut; try{ outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start(); }catch( IOException e ){ } } } • .

  39. 7.4 多线程问题---线程间的通信 class Reader extends Thread { private PipedInputStream inStream;//从中读数据 public Reader(PipedInputStream i) { inStream = i; } public void run() { String line; DataInputStream d; boolean reading = true; try{ d = new DataInputStream( inStream ); while( reading && d != null){ try{line = d.readLine(); if( line != null ){ System.out.println( ”Read: " + line ); } else reading = false; }catch( IOException e){ } } catch( IOException e ){ System.exit(0); } try{ Thread.sleep( 4000 );} catch( InterruptedException e ){}}}

  40. 7.4 多线程问题--线程间的通信 class Writer extends Thread { private PipedOutputStream outStream;//将数据输出 private String messages[ ]= { "Monday", "Tuesday ", "Wednsday", "Thursday","Friday :", "Saturday:","Sunday :"}; public Writer(PipedOutputStream o) { outStream = o; } public void run() { PrintStream p = new PrintStream( outStream ); for (int i = 0; i < messages.length; i++) { p.println(messages[ i ]); p.flush(); System.out.println("WrIte:" + messages[i] ); } p.close(); p = null; }} • .

  41. 小结 • 线程概念 • 线程状态及线程控制 • 线程优先级和线程调度 • 线程同步 • 线程通信 • 读写程序

  42. 习题 • 什么是线程?线程和进程的区别。 • 创建线程有哪些方法,如何使用? • 线程有哪几种状态?是如何进行控制的? • 请编写一个程序,实现在一分钟后显示当时的时间。 • 当我们编译下面的代码时,会发生什么情况?

  43. 习题 Public class Runt implements Runnable{ public static void main(String args[]){ Runt r1 = new Runt(); Thread t = new Thread(r1); t.start();} public void start(){ for(int i = 0;i<100;i++) System.out.println(i); } }

  44. 习题 • 创建两个线程的实例,分别将一个数组从小到大和从大到小排列.输出结果. • 当我们编译运行下面的代码时,会发生什么情况? Public class TGo implements Runnable{ public static void main(String args[]){ TGo r1 = new TGo(); Thread t = new Thread(r1); t.start();} public void run(){ while(true){ Thread.currentThread().sleep(1000); System.out.println(" looping while "); } }

  45. public class ThreadTest { public static void main( String args[] ) { int a[] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; for (int i=0;i<a.length;i++) System.out.print(a[i]+" "); System.out.println(); SortAsc threadA = new SortAsc( a ); SortDes threadD = new SortDes ( a ); threadA.start(); threadD.start(); } }

  46. class SortAsc extends Thread { int sort[]; public SortAsc( int temp[]) {sort = new int[temp.length]; System.arraycopy( temp,0,sort,0,temp.length); } public void run() {for ( int pass = 1; pass < sort.length; pass++ ) for ( int i = 0; i < sort.length - 1; i++ ) if ( sort[ i ] >sort[ i + 1 ] ) swap( sort, i, i + 1 ); for (int i=0;i<sort.length;i++) System.out.print(sort[i]+" "); System.out.println(); } public void swap( int c[], int first, int second ){ int hold; hold = c[ first ]; c[ first ] = c[ second ]; c[ second ] = hold; } }

  47. class SortDes extends Thread { int sort[]; public SortDes ( int temp[] ) {sort = new int[temp.length]; System.arraycopy( temp,0,sort,0,temp.length); } public void run() {for ( int pass = 1; pass < sort.length; pass++ ) for ( int i = 0; i < sort.length - 1; i++ ) if ( sort[ i ] <sort[ i + 1 ] ) swap( sort, i, i + 1 ); for (int i=0;i<sort.length;i++) System.out.print(sort[i]+" "); System.out.println(); } public void swap( int c[], int first, int second ){ int hold; hold = c[ first ]; c[ first ] = c[ second ]; c[ second ] = hold; } }

More Related