Presentation is loading. Please wait.

Presentation is loading. Please wait.

第7章 多线程 北京大学计算机系 代亚非.

Similar presentations


Presentation on theme: "第7章 多线程 北京大学计算机系 代亚非."— Presentation transcript:

1 第7章 多线程 北京大学计算机系 代亚非

2 第7章 多线程 7.1 多线程基本概念 7.2 创建线程的方式 7.3 线程的挂起与唤醒 7.4 多线程问题 7.5 小结

3 7.1 多线程基本概念 多线程的任务 传统的进程 文件 各种系统资源 输入输出装置 文件 各种系统资源 输入输出装置 数据区段 数据区段
7.1 多线程基本概念 文件 各种系统资源 输入输出装置 文件 各种系统资源 输入输出装置 数据区段 数据区段 程序区段 程序区段 同时有数个地方在执行 只有一个地方在执行 多线程的任务 传统的进程

4 7.1 多线程基本概念 多线程的优势: 减轻编写交互频繁、涉及面多的程序的困难. 程序的吞吐量会得到改善.
7.1 多线程基本概念 多线程的优势: 减轻编写交互频繁、涉及面多的程序的困难. 程序的吞吐量会得到改善. 由多个处理器的系统,可以并发运行不同的线程.(否则,任何时刻只有一个线程在运行)

5 7.1 多线程基本概念 线程与进程的区别: 多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响. 线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

6 7.1 多线程基本概念 对线程的综合支持是Java技术的一个重要特色.它提供了thread类、监视器和条件变量的技术.
7.1 多线程基本概念 对线程的综合支持是Java技术的一个重要特色.它提供了thread类、监视器和条件变量的技术. 虽然Macintosh,Windows NT,Windows 9等操作系统支持多线程,但若要用C或C++编写多线程程序是十分困难的,因为它们对数据同步的支持不充分.

7 7.2 创建线程的方式 1. public class mythread extends Applet implements Runnable (小应用或已经是某个类的子类时) 2. 继承类Thread public class mythread extends Thread 3. 上述两种方法中都可用类Thread产生线程的对象 Thread newthread; 4. 创建并启动线程 newthread=new Thread(this); newthread.start();

8 7.2 创建线程的方式 5. run方法是运行线程的主体,启动线程时,由java直接调用 public void run()
7.2 创建线程的方式 5. run方法是运行线程的主体,启动线程时,由java直接调用 public void run() 6.停止线程,由小应用程序的stop调用线程的stop newthread.stop() 7 sleep方法的作用,暂停线程的执行,让其它线程得到机会,sleep要丢出异常,必须抓住. Try{sleep(100)}catch(InterruptedException e){} 例:小应用程序中不用Runnable接口仍然可以使用线程(不调用主类的方法和调用主类的方法)

9 7.2 创建线程的方式 import java.applet.*; public class thread extends Applet
7.2 创建线程的方式 import java.applet.*; public class thread extends Applet { mythread t1=new mythread(); public init() { t1.start();} class mythread extends Thread { public void run() { for (int i=0;i<4;i++) System.out.println( “”+i); { try{sleep(400); } catch(InteruptedException e){ } }

10 7.2 创建线程的方式 public class mainclass extends Applet { C t1=new C(this);
public void init() { t1.start();} public void paint(Graphics g) { g.drawString("Hello,java",10,50);}} class C extends Thread { mainclass a; C(mainclass b) { a=b; } public void run() { while(true){ a.repaint(); try{sleep(400);} catch(InterruptedException e){} }} 7.2 创建线程的方式

11 7.2 创建线程的方式 8.其它常用的方法 isAlive :判断线程目前是否正在执行状态中
7.2 创建线程的方式 8.其它常用的方法 isAlive :判断线程目前是否正在执行状态中 if(newthread.isAlive()) newthread.stop(); resume:要求被暂停得线程继续执行 suspend:暂停线程的执行 join:等待线程执行完毕 thatThread.join();被等待的那个线程不结束,当前线程就一直等待. yield:将执行的权力交给其它线程,自己到队列的最后等待.

12 7.2 创建线程的方式 9.线程的优先权 某一时刻只有一个线程在执行,调度策略为固定优先级调度.
7.2 创建线程的方式 9.线程的优先权 某一时刻只有一个线程在执行,调度策略为固定优先级调度. newthread.setPriority(Thread.MIN_PRIORITY) 级别有:MIN-PRIORITY NOM_PRIORITY MAX-PRIORITY 10. 自私的线程:有很高的优先权的线程,不主动睡眠或让出处理器控制权.

13 7.2 创建线程的方式 11. 线程的状态 new Thread() New Thread Runnable start()
7.2 创建线程的方式 11. 线程的状态 new Thread() New Thread Runnable start() Not Runnable stop() Dead yield() stop() or run()exit . suspend() sleep() wait() resume()

14 7.2 创建线程的方式 当一个线程执行完所有语句后就自动终止,调用线程的stop()方法,也可以强制终止线程。
7.2 创建线程的方式 当一个线程执行完所有语句后就自动终止,调用线程的stop()方法,也可以强制终止线程。 如果希望线程正常终止,可采用标记来使线程中的run()方法退出。

15 7.2 创建线程的方式 public class Xyz implements Runnable
7.2 创建线程的方式 public class Xyz implements Runnable { private boolean timeToQuit=false; public void run() { while (!timeToQuit) {…..} //clean up before run() ends; } public void stopRunning() { timeToQuit=true;}

16 7.2 创建线程的方式 public class ControlThread { private Runnable r=new Xyz();
7.2 创建线程的方式 public class ControlThread { private Runnable r=new Xyz(); private Thread t=new Thread(r); public void startThread() { t.start(); } publi void stopThread() { r.stopRunning();} }

17 7.3 线程的挂起与唤醒 暂停线程的执行等待条件满足再执行. 下面的例子显示线程的挂起和唤醒 小应用程序第一次开始时,线程被启动
7.3 线程的挂起与唤醒 暂停线程的执行等待条件满足再执行. 下面的例子显示线程的挂起和唤醒 小应用程序第一次开始时,线程被启动 浏览器改变页面时,小应用程序的stop()方法被调用,线程被挂起. 浏览器回到原来的页面时,线程被唤醒.

18 7.3 线程的挂起与唤醒 public void start() { if (mythread==null)
7.3 线程的挂起与唤醒 public void start() { if (mythread==null) {mythread=new Thread(); mythread.start();} else { mythread.resume();} } public void run() { while(true){ try{sleep(100);} catch(InterruptedException e) {}} public void stop() { mythread.suspend(); }.

19 7.4 多线程问题---执行的顺序 多个线程运行时,调度策略为固定优先级调度.级别相同时,由操作系统按时间片来分配
7.4 多线程问题---执行的顺序 多个线程运行时,调度策略为固定优先级调度.级别相同时,由操作系统按时间片来分配 下面给出的例子中,共运行三个线程,它们做同样的事, 每次打印循环次数和自己的序列号,运行结果表明,它们并不是连续运行的. 在上例中如果给某个线程赋予较高的优先权,则发现这个进程垄断控制权 thread.setPriority(Thread.MAX_PRIORITY) thread\multithread.class--f1.bat thread\Priority.class---f2.bat

20 7.3 多线程问题 //多个进程运行时执行顺序是交叉的 class multithread extends Thread
{ int threadNum; public static void main(String args[]) { multithread array[]=new multithread[3]; for (int i=0;i<3;i++) array[i]=new multithread(i); for (int i=0;i<3;i++) array[i].start(); } multithread(int SerialNum) { super(); threadNum=SerialNum; } public void run() { for(int j=0;j<5;j++) System.out.println(“<"+j+"> +MySerialNum); System.out.println("thread "+threadNum+ "bye.");}} 7.3 多线程问题

21 7.4 多线程问题---如何写多线程 1.分别定义不同的线程类,在各自的run方法中定义线程的工作
7.4 多线程问题---如何写多线程 1.分别定义不同的线程类,在各自的run方法中定义线程的工作 class mythread1 extends Thread { public void run{….} } class mythread2 extends Thread 2. 在主类中实例化各线程类,并启动线程. public class demo extends Applet { public void init() { mythread t1=new mythread1(); mythread t2=new mythread2(); t1.start(); t2.start();} }

22 7.4 多线程问题---如何写多线程 练习:将窗口分为上下两个区,分别运行两个线程,一个在上面的区域中显示由右向左游动的字符串,另一个在下面的区域从左向右游动的字符串. 方法一: 一个线程,在paint方法中使用两个输出字符串的语句 public void paint(Graphics g) { if y1<0 y1=200 else y1=y1-10; if y2>200 y2=0 else y2=y2+10; g.drawString(“hello, Java!”,20,y1,); g.drawString(“hello, Java!”,40,y2,); }

23 7.4 多线程问题---如何写多线程 方法二:定义两个类,运行各自的线程,各自有自己的paint()方法.
7.4 多线程问题---如何写多线程 方法二:定义两个类,运行各自的线程,各自有自己的paint()方法. 注意: 两个小应用程序必须是panel类或者是canvas类,将小应用的区域分成两块,否则不能运行paint语句.

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

25 7.4 多线程问题---线程间的通信 管道流不能直 接读写 PrintStream p = new PrintStream( pos );
7.4 多线程问题---线程间的通信 管道流不能直 接读写 PrintStream p = new PrintStream( pos ); p.println(“hello”); DataInputStream d=new DataInputStream(pis); d.readLine(); 2. 通过一个中间类来传递信息. printStream DataInputStream 线程2 线程1 中间类m s m.write(s) s=m.read() write() read()

26 7.4 多线程问题--线程间的通信 管道流可以连接两个线程间的通信 下面的例子里有两个线程在运行,一个往外输出信息,一个读入信息.
将一个写线程的输出通过管道流定义为读线程的输入. outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start();

27 7.4 多线程问题--线程间的通信 (thread\Pipethread.class--f3.bat) 主类Pipethread
作为参数传给Writer Writer( outStream ) 辅类Writer 线 辅类 Reader 线 将数据写 到输出流 输入流 从流中读数据

28 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 多线程问题--线程间的通信 .

29 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 多线程问题---线程间的通信

30 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; }} 7.4 多线程问题--线程间的通信 .

31 7.3 多线程问题---资源协调 1. 数据的完整性 变量 余额 withdrwal() 透支 线程1 取过来 资源 加1后送回去 线程2
线程10 资源 取过来 加1后送回去 withdrwal() 透支 余额 变量

32 7.3 多线程问题---资源协调 对共享对象的访问必须同步,叫做条件变量.
Java语言允许通过监视器(有的参考书称其为管程)使用条件变量实现线程同步. 监视器阻止两个线程同时访问同一个条件变量.它的如同锁一样作用在数据上. 线程1进入withdrawal方法时,获得监视器(加锁);当线程1的方法执行完毕返回时,释放监视器(开锁),线程2的withdrawal方能进入. withdrawal() 线程1 监视器 线程2

33 7.3 多线程问题---资源协调 用synchronized来标识的区域或方法即为监视器监视的部分。
一般情况下,只在方法的层次上使用关键区 read write 线程1 线程2

34 7.3 多线程问题---资源协调 此处给出的例子演示两个线程在同步限制下工作的情况. class Account
{ statics int balance=1000; //为什么用static? statics int expense=0; public synchronized void withdrawl(int amount) { if (amount<=balance) { balance-=amount; expense+=amount;} else { System.out.println(“bounced: “+amount);} }

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

36 7.3 多线程问题---资源协调 notify的作用是唤醒正在等待同一个监视器的线程. wait的作用是让当前线程等待 信息版例子
read()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程. write()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程. DemoWait.class--->f4.bat

37 7.3 多线程问题---资源协调 writer reader aaaa aaaa aaaa aaaa aaaa aaaa aaaa bbbbb bbbbb bbbbb cccc cccc cccc cccc cccc cccc

38 7.3 多线程问题---资源协调 class WaitNotifyDemo {
public static void main(String[] args) { { MessageBoard m = new MessageBoard(); Reader readfrom_m = new Reader(m); Writer writeto_m=new Writer(m); readfrom_m.start(); writeto_m.start(); }

39 7.3 多线程问题---资源协调 class MessageBoard { { private String message;
private boolean ready = false;(信号灯) public synchronized String read() { while (ready == false) { try { wait(); } catch (InterruptedException e) { } } ready = false; notify(); //起始状态先写后读 return message; } public synchronized void write(String s) { while (ready == true) { try { wait(); } catch (InterruptedException e) { } } message = s; ready = true; notify(); }} 7.3 多线程问题---资源协调

40 7.3 多线程问题---资源协调 class Reader extends Thread
{ private MessageBoard mBoard; public Reader(MessageBoard m) { mBoard = m; } public void run() { String s = " "; boolean reading = true; while( reading ){ s = mBoard.read(); System.out.println("Reader read: " + s); if( s.equals("logoff") ) reading = false; } System.out.println("Finished: 10 seconds..."); try{ sleep( ); } catch (InterruptedException e) { } } } 7.3 多线程问题---资源协调

41 7.3 多线程问题---资源协调 class Writer extends Thread
{ private MessageBoard mBoard; private String messages[ ]= { "Monday : ", “…..”, "Sunday : "}; public Writer(MessageBoard m) { mBoard = m; } public void run() { { for (int i = 0; i < messages.length; i++) { mBoard.write(messages[ i ]); System.out.println("Writer wrote:" + messages[i] ); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } } mBoard.write("logoff"); } } 7.3 多线程问题---资源协调

42 7.3 多线程问题---资源协调 多线成问题---资源的协调和锁定 1. 死锁问题 如果你的持有一个锁并试图获取另一个锁时,就有死锁的危险.
解决死锁问题的方法:给条件变量施加排序 线程2 pen 线程1 note 把“pen”给我,我 才能给你“note” 把“note”给我,我 才能给你“pen”

43 7.3 多线程问题---daemon线程 什么是daemon(守护)?
在客户/服务器模式下,服务器的作用是等待用户发来请求,并按请求完成客户的工作 守护线程是为其它线程提供服务的线程 守护线程一般应该是一个独立的线程,它的run()方法是一个无限循环. 守护线程与其它线程的区别是,如果守护线程是唯一运行着的线程,程序会自动退出 request 服务器端 客户端 daemon

44 7.4 小结 1. 实现线程有两种方法: 实现Ruannable接口 继承Thread类 2. 在小应用中通常在start中创建线程
3.当新线程被启动时,java调用该线程的run方 法,它是Thread的核心. 4. 线程由四个状态:新生,运行,暂停,死亡 5. 线程间的通信方式由三种:完全共享数据,通过监视器,通过join.

45 7.4 小结 6. 两个或多个线程竞争资源时,需要用同步的方法协调资源. 7. 多个线程执行时,要用到同步方法,即使用
synchronized的关键字设定同步区 8. wait和notify起协调作用 9. 守护进程的特点是当程序中制胜它自己时,会自动中止.

46 作业 创建两个线程的实例,分别将一个数组从小大大和从达到小排列.输出结果.


Download ppt "第7章 多线程 北京大学计算机系 代亚非."

Similar presentations


Ads by Google