480 likes | 635 Views
第九章 多线程程序设计. §9.1 线程的概念 §9.2 Java 线程的创建 §9.3 线程状态和线程控制 §9.4 线程优先级和线程的调度 §9.5 线程同步 §9.6 线程的死锁. §9.1 线程的概念. 进程:一个执行中的程序,有自己独立的内存空间等系统资源。 线程:程序中单个顺序的流控制,单个程序中多个线程共享系统资源。 多线程编程:将任务分成多个并发的子任务。 Java 支持多线程。. §9.2 Java 线程的创建. 继承 Thread 类的方法 实现 Runnable 接口的方法
E N D
第九章 多线程程序设计 §9.1 线程的概念 §9.2 Java 线程的创建 §9.3 线程状态和线程控制 §9.4 线程优先级和线程的调度 §9.5 线程同步 §9.6 线程的死锁
§9.1 线程的概念 • 进程:一个执行中的程序,有自己独立的内存空间等系统资源。 • 线程:程序中单个顺序的流控制,单个程序中多个线程共享系统资源。 • 多线程编程:将任务分成多个并发的子任务。 • Java支持多线程。
§9.2 Java 线程的创建 • 继承Thread类的方法 • 实现Runnable接口的方法 线程体 有run方法(一般是一个循环,需 要以线程方式运行的代码)
1 继承Thread类的方法 • 通过实现Thread类的子类、并置换其中的run()方法定义线程体,然后创建该子类的对象创建线程。如: public class Counter extends Thread{ … public void run(){ //定义线程的行为 } … } • 启动线程 Counter threadCounter = new Counter(); threadCounter.start();
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();
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:表示新线程的名字
3 Thread类构造器和Threadgroup类 • Threadgroup 类(线程组):所有的线程一定属于某个线程组。属于Java.lang包。用于进行统一管理。 • 显式:ThreadGroup myThreadGroup = new ThreadGroup(“my group of threads”) • 隐式:新创建的线程自动的属于创建该线程的线程所在的线程组。
例 线程概念例子 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 ); }
例 线程概念例子 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" ); } }
例 线程概念例子 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" ); } }
§9.3 线程状态和线程控制 Runnable状态 Running状态 Not Runnable状态 Dead状态 New Thread状态 改变 对线程进行各种控制 Thread类中的方法实现 • 类Thread的常用方法 • 控制和状态的关系 • 例子 线程状态
2 控制和状态的关系 Start() 获得互斥使用权 创建 可运行 线程结束 或时间到 或interupt() 其它 阻塞 Yield() 调度时间到 Sleep() Join() 互斥阻塞 运行中 Synchronized Notify() Interrupt() Wait() 运行结束 等待阻塞 不可运行 死亡
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();
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; } }
3 例子—时钟 <html> <applet code = "Clock.class" width = 50 height = 40> </applet> </html>
§9.4 线程优先级和线程的调度 • 线程的调度取决于线程的优先级。采用先占式调度,先占调度有分为: • 独占方式 • 分时方式 • 线程的优先级可以用setPriority()显式进行设置。getPriority()获得优先级。 • 线程优先级用整数表示。从1到10,Thread.MIN_PRIORITY(1), Thread.MAX_PRIORITY(10), Thread.NORM_PRIORITY(5) • 例
§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){
§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); }
§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){ } } }
§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++; } }
§9.5 线程同步-- 数据的完整性 线程2 监视器 线程1 withdrawal() • 因多线程并发而引起执行顺序的不确定性,执行的不确定性会产生执行结果的不确定性。在多个线程需要共享数据时通常会产生这种不确定性。对共享对象的访问必须同步,叫做条件变量. • Java语言允许通过监视器(有的参考书称其为管程)使用条件变量实现线程同步. • 监视器阻止两个线程同时访问同一个条件变量.它如同锁一样作用在数据上. • 线程1进入withdrawal方法时,获得监视器(加锁);当线程1的方法执行完毕返回时,释放监视器(开锁),线程2的withdrawal方能进入.
§9.5 线程同步-- 数据的完整性 线程1 线程2 监 视 器 read write • 用synchronized来标识的区域或方法即为监视器监视的部分。 • 一个类或一个对象有一个监视器,如果一个程序内有两个方法使用synchronized标志,则他们在一个监视器管理之下. • 一般情况下,只在方法的层次上使用关键区
§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);} } }
§9.5 线程同步--等待同步数据 . 生产者 消费者 共享对象 write . read 可能出现的问题: • 生产者比消费者快时,消费者会漏掉一些数据没有取到 • 消费者比生产者快时,消费者取相同的数据. • notify()和wait ()方法用来协调读取的关系. • notify()和wait ()都只能从同步方法中的调用.
§9.5 线程同步--等待同步数据 • notify的作用是唤醒正在等待同一个监视器的线程. • wait的作用是让当前线程等待 • read()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程. • write()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程.
例 生产者/消费者例子(有同步) // 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 );
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
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 ); }
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;
} 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(); } }
§9.6 线程的死锁 把“pen”给我,我 才能给你“note” 线程2 线程1 pen note 把“note”给我,我 才能给你“pen” • 死锁:指两个或多个线程无止境地相互等待的过程。错误的同步往往会引起死锁。 • 程序员认真设计避免死锁。如果你持有一个锁并试图获取另一个锁时,就有死锁的危险. • 解决死锁问题的方法:对共享资源访问的顺序,即给条件变量施加排序。
§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);
§9.7 多线程问题---线程间的通信 printStream DataInputStream • 管道流不能直 接读写 PrintStream p = new PrintStream( pos ); p.println(“hello”); DataInputStream d=new DataInputStream(pis); d.readLine(); • 管道流可以连接两个线程间的通信 • 将一个写线程的输出通过管道流定义为读线程的输入.
§9.7多线程问题--线程间的通信 主类Pipethread 作为参数传给Writer Writer( outStream ) 辅类Writer 线 程 类 管 道 流 辅类 Reader 线 程 类 将数据写 到输出流 输入流 从流中读数据
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 ){ } } } • .
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 ){}}}
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; }} • .
小结 • 线程概念 • 线程状态及线程控制 • 线程优先级和线程调度 • 线程同步 • 线程通信 • 读写程序
习题 • 什么是线程?线程和进程的区别。 • 创建线程有哪些方法,如何使用? • 线程有哪几种状态?是如何进行控制的? • 请编写一个程序,实现在一分钟后显示当时的时间。 • 当我们编译下面的代码时,会发生什么情况?
习题 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); } }
习题 • 创建两个线程的实例,分别将一个数组从小到大和从大到小排列.输出结果. • 当我们编译运行下面的代码时,会发生什么情况? 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 "); } }
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(); } }
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; } }
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; } }