《Java第九章多线程程序设计基础篇》由会员分享,可在线阅读,更多相关《Java第九章多线程程序设计基础篇(48页珍藏版)》请在金锄头文库上搜索。
1、第九章第九章 多线程程序设计多线程程序设计9.1 9.1 线程的概念线程的概念9.2 Java 9.2 Java 线程的创建线程的创建9.3 9.3 线程状态和线程控制线程状态和线程控制9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度9.5 9.5 线程同步线程同步9.6 9.6 线程的死锁线程的死锁9.1 9.1 线程的概念线程的概念进程:一个执行中的程序,有自己独立的进程:一个执行中的程序,有自己独立的内存空间等系统资源。内存空间等系统资源。线程:程序中单个顺序的流控制,单个程线程:程序中单个顺序的流控制,单个程序中多个线程共享系统资源。序中多个线程共享系统资源。多线程编程:将
2、任务分成多个并发的子任多线程编程:将任务分成多个并发的子任务。务。JavaJava支持多线程。支持多线程。9.2 Java 9.2 Java 线程的创建线程的创建继承继承ThreadThread类的方法类的方法实现实现RunnableRunnable接口的方法接口的方法线程体线程体 有有runrun方法(一般是一个循环,需方法(一般是一个循环,需 要以线程方式运行的代码)要以线程方式运行的代码)1 1 继承继承ThreadThread类的方法类的方法1)1)通过实现通过实现ThreadThread类的子类、并置换其中的类的子类、并置换其中的runrun()()方方法定义线程体,然后创建该子类的
3、对象创建线程。法定义线程体,然后创建该子类的对象创建线程。如:如: public class Counter extends Threadpublic class Counter extends Threadpublic void run() /public void run() /定义线程的行为定义线程的行为 2)2)启动线程启动线程Counter Counter threadCounterthreadCounter = new Counter(); = new Counter();threadCounterthreadCounter.start();.start();2 2 实现实现Run
4、nableRunnable接口的方法接口的方法1)1)通过在类中实现通过在类中实现RunnableRunnable接口,并在该类中提供接口,并在该类中提供runrun()()方法的实现。如:方法的实现。如: public class Counter implements public class Counter implements RunnableRunnable public void run() /public void run() /定义线程的行为定义线程的行为 2)2)启动线程启动线程1)1)Counter C1 = new Counter();Counter C1 = new Co
5、unter();new Thread(C1).start();new Thread(C1).start();2)2)Counter C1 = new Counter();Counter C1 = new Counter();Thread t1 = new Thread(C1);Thread t1 = new Thread(C1);t1.start();t1.start();3 Thread3 Thread类构造器和类构造器和ThreadgroupThreadgroup类类Thread Thread 类构造器:类构造器:Thread()Thread()Thread(Runnable)Thread
6、(Runnable)Thread(Threadgroup,Runnable)Thread(Threadgroup,Runnable)Thread(String)Thread(String)Thread(Threadgroup,String)Thread(Threadgroup,String)Thread(Runnable,String)Thread(Runnable,String)Thread(Threadgroup,Runnable,String)Thread(Threadgroup,Runnable,String)Runnable:Runnable:类的实例,调用类的实例,调用RunRun
7、()方法的对象()方法的对象ThreadgroupThreadgroup:新创建的线程所属的线程组:新创建的线程所属的线程组StringString:表示新线程的名字:表示新线程的名字3 Thread3 Thread类构造器和类构造器和ThreadgroupThreadgroup类类Threadgroup Threadgroup 类(线程组):所有的线程一定属类(线程组):所有的线程一定属于某个线程组。属于于某个线程组。属于Java.langJava.lang包。用于进行统一包。用于进行统一管理。管理。显式:显式:ThreadGroup myThreadGroup = new ThreadGr
8、oup myThreadGroup = new ThreadGroup(“my group of threads”)ThreadGroup(“my group of threads”)隐式:新创建的线程自动的属于创建该线程的隐式:新创建的线程自动的属于创建该线程的线程所在的线程组。线程所在的线程组。例例 线程概念例子线程概念例子class PrintThread extends Thread private int sleepTime; public PrintThread( String name ) super( name ); sleepTime = (int) ( Math.rando
9、m() * 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( ge
10、tName() + 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( t
11、hread4 ); System.out.println( nStarting threads ); thread1.start(); thread2.start(); thread3.start(); thread4.start(); System.out.println( Threads startedn ); 9.3 9.3 线程状态和线程控制线程状态和线程控制类类ThreadThread的常用方法的常用方法控制和状态的关系控制和状态的关系例子例子线程状态线程状态对线程进行各种控制对线程进行各种控制Thread类中的方法实现类中的方法实现改变改变Runnable状态状态Running状态
12、状态Not Runnable状态状态Dead状态状态New Thread状态状态1 1 类类ThreadThread的常用方法的常用方法方法方法简要说明简要说明isAlive()判断线程目前是否已被启动并且未被终止判断线程目前是否已被启动并且未被终止wait()使当前线程处于等待状态使当前线程处于等待状态notify()将正在等待当前管程的线程唤醒将正在等待当前管程的线程唤醒Suspend()暂停线程的执行暂停线程的执行resume()被方法被方法SuspendSuspend()暂停的线程继续执行()暂停的线程继续执行start()开始线程的执行开始线程的执行run()线程中真正执行的程序块线
13、程中真正执行的程序块stop()结束线程的执行结束线程的执行sleep()让目前正在执行的线程小睡片刻让目前正在执行的线程小睡片刻yield()自愿将执行的权利交给其它同优先级线程自愿将执行的权利交给其它同优先级线程1 1 类类ThreadThread的常用方法的常用方法方法方法简要说明简要说明join()暂停当前线程的执行,等待调用该方法的线暂停当前线程的执行,等待调用该方法的线程结束后在继续执行本线程程结束后在继续执行本线程interrupt()中断线程组中所有线程或当前线程中断线程组中所有线程或当前线程currentThread()返回当前执行线程的引用对象返回当前执行线程的引用对象 s
14、etName()设置线程的名字设置线程的名字getName()返回线程的名字返回线程的名字activeCount()返回当前线程所在线程组中活动线程的个数返回当前线程所在线程组中活动线程的个数getThreadGroup()返回当前线程所属的线程组名返回当前线程所属的线程组名setDaemon()设置当前线程为设置当前线程为DaemonDaemon线程线程isDaemon()测试线程是否为测试线程是否为DaemonDaemon线程线程1 1 类类ThreadThread的常用方法的常用方法方法方法简要说明简要说明toString()返回线程的字符串信息,包括线程的名字、返回线程的字符串信息,包
15、括线程的名字、优先级和线程组优先级和线程组enumerate()把当前线程的线程组中的活动线程拷贝到线把当前线程的线程组中的活动线程拷贝到线程数组中,包括子线程程数组中,包括子线程checkAccess()确定当前线程是否允许修改该线程确定当前线程是否允许修改该线程2 控制和状态的关系控制和状态的关系创建创建可运行可运行运行中运行中死亡死亡其它其它阻塞阻塞互斥阻塞互斥阻塞等待阻塞等待阻塞Start()调度时间到调度时间到运行结束运行结束Yield()Wait()Notify()Interrupt()SynchronizedSleep() Join()线程结束线程结束或时间到或时间到或或inte
16、rupt()获得互斥使用权获得互斥使用权不可运行不可运行3 3 例子例子时钟时钟import java.awt.Graphics;import java.util.*;import java.applet.Applet ;public class Clock extends Applet implements RunnableThread clockThread;public void start()if(clockThread = null)clockThread = new Thread(this,Clock);clockThread.start();public void run()wh
17、ile(clockThread!=null)repaint();3 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
18、.get(Calendar.SECOND),5,10);public void stop()clockThread = null; 3 3 例子例子时钟时钟 9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度线程的调度取决于线程的优先级。采用先占式调度,线程的调度取决于线程的优先级。采用先占式调度,先占调度有分为:先占调度有分为:独占方式独占方式分时方式分时方式线程的优先级可以用线程的优先级可以用setPriority()setPriority()显式进行设置。显式进行设置。getPrioritygetPriority()()获得优先级。获得优先级。线程优先级用整数表示。从线程优先级
19、用整数表示。从1 1到到1010,Thread.MIN_PRIORITY(1), Thread.MIN_PRIORITY(1), Thread.MAX_PRIORITY(10), Thread.MAX_PRIORITY(10), Thread.NORM_PRIORITY(5)Thread.NORM_PRIORITY(5)例例9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度画线画线import java.applet.Applet;import java.awt.Color;public class RaceApplet extends Applet implements Runna
20、blefinal static int NUMRUNNERS = 2;final static int SPACING = 20;Runner runners = new RunnerNUMRUNNERS;Thread updateThread = new Thread(this,control);public void init()for(int i=0;iNUMRUNNERS;i+) runnersi = new Runner(); runnersi.setPriority(i+1);public boolean mouseDown(java.awt.Event evt,int x,int
21、 y)9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度if(!updateThread.isAlive() updateThread.start();for(int i=0;iNUMRUNNERS;i+)if(!runnersi.isAlive() runnersi.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;iNUMRUNNERS;i
22、+) int pri = runnersi.getPriority(); g.drawString(new Integer(pri).toString(),0,(i+1)*SPACING);update(g); 9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度public void update(java.awt.Graphics g )for(int i=0;iNUMRUNNERS;i+) g.drawLine(SPACING,(i+1)*SPACING,SPACING+(runnersi.tick)/1000,(i+1)*SPACING);public void run()wh
23、ile(updateThread!=null)repaint();try updateThread.sleep(100);catch(InterruptedException e) 9.4 9.4 线程优先级和线程的调度线程优先级和线程的调度public void stop()for(int i=0;iNUMRUNNERS;i+) if(runnersi.isAlive()runnersi = null;if(updateThread.isAlive() updateThread = null; Class Runner extends Threadpublic int tick = 1;pu
24、blic void run()while(tick400000) tick+;9.5 线程同步线程同步- 数据的完整性数据的完整性因多线程并发而引起执行顺序的不确定性,执行的不确定性会因多线程并发而引起执行顺序的不确定性,执行的不确定性会产生执行结果的不确定性。在多个线程需要共享数据时通常会产生执行结果的不确定性。在多个线程需要共享数据时通常会产生这种不确定性。对共享对象的访问必须同步产生这种不确定性。对共享对象的访问必须同步, ,叫做条件变量叫做条件变量. .JavaJava语言允许通过监视器语言允许通过监视器( (有的参考书称其为管程有的参考书称其为管程) )使用条件变使用条件变量实现线程
25、同步量实现线程同步. .监视器阻止两个线程同时访问同一个条件变量监视器阻止两个线程同时访问同一个条件变量. .它如同锁一样作它如同锁一样作用在数据上用在数据上. .线程线程1 1进入进入withdrawalwithdrawal方法时方法时, ,获得监视器获得监视器( (加锁加锁););当线程当线程1 1的方的方法执行完毕返回时法执行完毕返回时, ,释放监视器释放监视器( (开锁开锁),),线程线程2 2的的withdrawalwithdrawal方能方能进入进入. .withdrawal()线程线程1监视器监视器线程线程29.5 线程同步线程同步- 数据的完整性数据的完整性用用synchron
26、ized来标识的区域或方法即为监视器监来标识的区域或方法即为监视器监视的部分。视的部分。一个类或一个对象有一个监视器一个类或一个对象有一个监视器,如果一个程序内有如果一个程序内有两个方法使用两个方法使用synchronized标志标志,则他们在一个监则他们在一个监视器管理之下视器管理之下.一般情况下,只在方法的层次上使用关键区一般情况下,只在方法的层次上使用关键区readwrite监监视视器器线程线程1线程线程29.5 线程同步线程同步- 数据的完整性数据的完整性此处给出的例子演示两个线程在同步限制下工作的情况此处给出的例子演示两个线程在同步限制下工作的情况.class Account sta
27、tic 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 线程同步线程同步-等待同步数据等待同步数据生产者生产者消费者消费者.共享对象共享对象writeread可能出现的问题可能出现的问题:生产者比消费者快时生产者比消费者快时,消费者会
28、漏掉一些数据消费者会漏掉一些数据没有取到没有取到消费者比生产者快时消费者比生产者快时,消费者取相同的数据消费者取相同的数据.notify()和和wait ()方法用来协调读取的关系方法用来协调读取的关系.notify()和和wait ()都只能从同步方法中的调用都只能从同步方法中的调用.9.5 线程同步线程同步-等待同步数据等待同步数据notify的作用是唤醒正在等待同一个监视的作用是唤醒正在等待同一个监视器的线程器的线程.wait的作用是让当前线程等待的作用是让当前线程等待read()方法在读信息之前先等待方法在读信息之前先等待,直到信息直到信息可读可读,读完后通知要写的线程读完后通知要写的
29、线程.write()方法在写信息之前先等待方法在写信息之前先等待,直到信直到信息被取走息被取走,写完后通知要读的进程写完后通知要读的进程.例例 生产者生产者/ /消费者例子(有同步)消费者例子(有同步)/ HoldIntegerSynchronized.javapublic class HoldIntegerSynchronized private int sharedInt = -1; private boolean writeable = true; / condition variable public synchronized void setSharedInt( int val )
30、while ( !writeable ) / not the producers 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 synchroniz
31、ed int getSharedInt() while ( writeable ) / not the consumers 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 ); re
32、turn sharedInt; / ProduceInteger.javapublic 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 ); Syste
33、m.err.println( getName() + finished producing values + nTerminating + getName() ); / ConsumeInteger.javapublic class ConsumeInteger extends Thread private HoldIntegerSynchronized cHold; public ConsumeInteger( HoldIntegerSynchronized h ) super( ConsumeInteger ); cHold = h; public void run() int val,
34、sum = 0; do val = cHold.getSharedInt(); sum += val; while ( val != 10 ); System.err.println(getName() + retrieved values totaling: + sum + nTerminating + getName() ); / SharedCell.javapublic class SharedCell public static void main( String args ) HoldIntegerSynchronized h = new HoldIntegerSynchroniz
35、ed(); ProduceInteger p = new ProduceInteger( h ); ConsumeInteger c = new ConsumeInteger( h ); p.start(); c.start(); 9.6 9.6 线程的死锁线程的死锁死锁:指两个或多个线程无止境地相互等待的过程。错误的死锁:指两个或多个线程无止境地相互等待的过程。错误的同步往往会引起死锁。同步往往会引起死锁。程序员认真设计避免死锁。程序员认真设计避免死锁。如果你持有一个锁并试图获取另如果你持有一个锁并试图获取另一个锁时一个锁时,就有死锁的危险就有死锁的危险.解决死锁问题的方法解决死锁问题的方法
36、:对共享资源访问的顺序对共享资源访问的顺序, ,即即给条件变量给条件变量施加排序。施加排序。线程线程2pen线程线程1note把把“pen”给我给我,我我才能给你才能给你“note”把把“note”给我给我,我我才能给你才能给你“pen”9.7 9.7 多线程问题多线程问题-线程间的通信线程间的通信1. 线程间的通信可以用管道流线程间的通信可以用管道流创建管道流创建管道流:PipedInputStream pis=new PipedInputStream();PipedOutputStream pos=new PipedOutputStream(pis);或或:PipedOutputStrea
37、m pos=new PipedOutputStream();PipedInputStream pis=new PipedInputStream(pos);线程线程1PipedOutputStream PipedInputStream输出流输出流outStream输入流输入流inStream线程线程29.7 9.7 多线程问题多线程问题-线程间的通信线程间的通信管道管道流不能直流不能直 接读写接读写PrintStream p = new PrintStream( pos );p.println(“hello”);DataInputStream d=new DataInputStream(pis)
38、;d.readLine();管道流可以连接两个线程间的通信管道流可以连接两个线程间的通信将一个写线程的输出通过管道流定义为读线程将一个写线程的输出通过管道流定义为读线程的输入的输入.printStream DataInputStream多线程问题多线程问题-线程间的通信线程间的通信主类主类Pipethread辅类辅类Writer线线程程类类辅类辅类Reader线线程程类类管管道道流流将数据写将数据写到输出流到输出流从流中读数据从流中读数据输入流输入流作为参数传给作为参数传给WriterWriter( outStream )7.4 多线程问题多线程问题-线程间的通信线程间的通信.public c
39、lass 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); ne
40、w 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
41、 = true; try d = new DataInputStream( inStream ); while( reading & d != null) tryline = 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.
42、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( outS
43、tream ); for (int i = 0; i messages.length; i+) p.println(messages i ); p.flush(); System.out.println(WrIte: + messagesi ); p.close(); p = null; 小结小结线程概念线程概念线程状态及线程控制线程状态及线程控制线程优先级和线程调度线程优先级和线程调度线程同步线程同步线程通信线程通信读写程序读写程序习题习题1.什么是线程?线程和进程的区别。什么是线程?线程和进程的区别。2.创建线程有哪些方法,如何使用?创建线程有哪些方法,如何使用?3.线程有哪几种状态?是如
44、何进行控制的?线程有哪几种状态?是如何进行控制的?4.请编写一个程序,实现在一分钟后显示当请编写一个程序,实现在一分钟后显示当时的时间。时的时间。5.当我们编译下面的代码时,会发生什么情当我们编译下面的代码时,会发生什么情况?况?习题习题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;i100;i+) Sys
45、tem.out.println(i); 习题习题6.创建两个线程的实例创建两个线程的实例,分别将一个数组从小到大和从分别将一个数组从小到大和从大到小排列大到小排列.输出结果输出结果.7.当我们编译运行下面的代码时,会发生什么情况?当我们编译运行下面的代码时,会发生什么情况?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) T
46、hread.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;ia.length;i+) System.out.print(ai+ ); System.out.println(); SortAsc threadA = new SortAsc( a ); SortDes thr
47、eadD = new SortDes ( a ); threadA.start(); threadD.start(); class SortAsc extends Thread int sort; public SortAsc( int temp) sort = new inttemp.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 i + 1 ) swap(
48、sort, i, i + 1 ); for (int i=0;isort.length;i+) System.out.print(sorti+ ); 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 inttemp.len
49、gth; 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;isort.length;i+) System.out.print(sorti+ ); System.out.println(); public void swap( int c, int first, int second ) int hold; hold = c first ; c first = c second ; c second = hold;