JAVA程序设计_Java线程_第1页
JAVA程序设计_Java线程_第2页
JAVA程序设计_Java线程_第3页
JAVA程序设计_Java线程_第4页
JAVA程序设计_Java线程_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

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

文档简介

1、第第8章章 Java线程线程v学习导读学习导读 本章将介绍本章将介绍Java线程编程技术,以及多线线程编程技术,以及多线程的同步和互斥。程的同步和互斥。 第第8章章 Java线程线程v线程基础线程基础v线程的生命线程的生命v多线程共享数据多线程共享数据v多线程的互斥和同步多线程的互斥和同步v本章小结本章小结线程的概念线程的概念v多任务:计算机在看上去几乎同一时间内多任务:计算机在看上去几乎同一时间内运行多个程序。运行多个程序。v多线程:单个程序内部也可以在同一时间多线程:单个程序内部也可以在同一时间进行多种运算。进行多种运算。v一个线程是一个程序内部的顺序控制流。一个线程是一个程序内部的顺序控

2、制流。不是程序,自己本身不能运行,必须在程序中不是程序,自己本身不能运行,必须在程序中运行。运行。如何在一个程序内部实现多个线程。如何在一个程序内部实现多个线程。v线程和进程线程和进程每个进程都有独立的代码和数据空间每个进程都有独立的代码和数据空间(进程上进程上下文下文),进程切换的开销大。,进程切换的开销大。线程:轻量的进程,同一类线程共享代码和数线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数据空间,每个线程有独立的运行栈和程序计数器器(PC),线程切换的开销小。,线程切换的开销小。多进程:在操作系统中,能同时运行多进程:在操作系统中,能同时运行多个任务多个

3、任务(程序程序)。多线程:在多线程:在同一同一应用程序中,有应用程序中,有多个顺序流多个顺序流同同时执行时执行。线程的概念模型线程的概念模型v虚拟的虚拟的CPU,封装在,封装在java.lang.Thread类类中。中。vCPU所执行的代码,传递给所执行的代码,传递给Thread类。类。vCPU所处理的数据,传递给所处理的数据,传递给Thread类。类。线程体线程体vJava的线程是通过的线程是通过java.lang.Thread类类来来实现的。实现的。v当我们生成一个当我们生成一个Thread类或者它的子类的类或者它的子类的对象后,一个新的线程就诞生了。对象后,一个新的线程就诞生了。v每个线

4、程都是通过某个特定每个线程都是通过某个特定Thread对象的对象的方法方法run( )来完成其操作的,方法来完成其操作的,方法run( )称称为为线程体线程体。线程的状态线程的状态唤醒唤醒挂起挂起,睡眠或等待睡眠或等待v创建状态创建状态(new Thread)Thread myThread = new MyThreadClass( );v可运行状态可运行状态( Runnable ) /非运行态非运行态Thread myThread = new MyThreadClass( );myThread.start( );这一状态并不是运行中状态(这一状态并不是运行中状态(Running),),因为也许

5、线程并未真正执行,只有被调度执因为也许线程并未真正执行,只有被调度执行时,才真正处于运行状态。行时,才真正处于运行状态。v不可运行状态(不可运行状态(Not Runnable,Blocked)调用了调用了sleep()方法方法; /此时线程的此时线程的run()方法将中断执行方法将中断执行调用了调用了suspend()方法方法; /挂起线程,不推荐使用挂起线程,不推荐使用为等候一个条件变量,线程调用为等候一个条件变量,线程调用wait()方法方法; I/O处发生线程阻塞处发生线程阻塞; /I/O阻塞阻塞v对于上面四种情况,都有可以返回对于上面四种情况,都有可以返回可运行态可运行态的方法与之对应

6、。的方法与之对应。 1. sleep()方法中的参数为休息时间,单位为毫秒,时间过方法中的参数为休息时间,单位为毫秒,时间过去后,线程即为去后,线程即为可运行可运行的。的。 /不是运行态,需再次被调度不是运行态,需再次被调度2. 一个线程调用一个线程调用suspend()方法后,只能有其它线程调用方法后,只能有其它线程调用它的它的resume()方法恢复。方法恢复。 3. 如果一个线程等待条件变量,如果要停止等待,需要条如果一个线程等待条件变量,如果要停止等待,需要条件变量所在的对象调用件变量所在的对象调用notify()或者或者 notifyAll()方法。方法。 4. 特定的特定的I/O指

7、令完成,指令完成,结束结束不可运行状态。不可运行状态。v死亡状态(死亡状态(Dead)线程的终止一般可通过两种方法实现:自然撤销(线程执线程的终止一般可通过两种方法实现:自然撤销(线程执行完)或是被停止(调用行完)或是被停止(调用stop()方法,不推荐使用)。方法,不推荐使用)。v自然撤销是线程的自然撤销是线程的 run() 方法正常退出。如下面的程序:方法正常退出。如下面的程序: public void run () int i=0; for ( i=0;i10;i+ ) System.out.println(i); t.start (); /t 是一个线程是一个线程 . /省略了一些语句

8、省略了一些语句 t.stop ();/调用调用 stop ()终止线程终止线程v其它注意事项其它注意事项1. 非法状态处理非法状态处理 v对于任何状态,如果调用的对于任何状态,如果调用的方法和状态不符方法和状态不符,都会,都会引起非法状态处理。例如,线程刚创建后,只能调引起非法状态处理。例如,线程刚创建后,只能调用用 start()或者或者 stop()方法,如果调用其它方法就会方法,如果调用其它方法就会引起非法状态处理。引起非法状态处理。 2. isAlive()方法方法 v在类在类Thread中提供了方法中提供了方法isAlive(),如果线程,如果线程已经已经启动启动(start),但是

9、未终止(),但是未终止(dead),返回),返回true,反之,返回反之,返回 false,表示该线程未启动,或者已终止。,表示该线程未启动,或者已终止。 v如果如果isAlive()方法返回方法返回true,不能区分不能区分是可运行态是可运行态(runnable)还是不可运行态()还是不可运行态(not runnable)。)。顺序执行顺序执行(单线程单线程)程序程序public class SequentialDemopublic static void main(String args)new Sequential(“C).run();new Sequential(“D).run();c

10、lass Sequential extends Thread String name=null;public Sequential(String n)name=n;public void run()for(int i=0;i5;i+)try / 睡眠一段随机时间睡眠一段随机时间Thread.sleep(long)(Math.random() * 1000);catch(InterruptedException e)e.printStackTrace();System.out.print(name);每次总输出结果:每次总输出结果:CCCCCDDDDD线程体的构造线程体的构造v(1) publi

11、c Thread(); /创建一个线程对象创建一个线程对象v(2) public Thread(Runnable target); /参数参数target的的run()方法方法将被线程对象调用将被线程对象调用v(3) public Thread(String name); /线程的名称线程的名称v(4) public Thread(Runnable target, String name); v(5) public Thread( ThreadGroup group, Runnable target); v(6) public Thread( ThreadGroup group, String

12、 name); v(7) public Thread( ThreadGroup group, Runnable target, String name );group指明线程所在的组,可以把很多线程加在一个组里面,指明线程所在的组,可以把很多线程加在一个组里面,进行集中控制。进行集中控制。target是执行线程体的目标对象。生成这个对象的类,实现是执行线程体的目标对象。生成这个对象的类,实现了了Runnable接口。其中包含线程体接口。其中包含线程体run()方法。方法。name是线程的名称。是线程的名称。v任何实现接口任何实现接口Runnable的对象都可以作为一个线程的对象都可以作为一个线

13、程的目标对象;的目标对象;v构造线程体的两种方法构造线程体的两种方法(1)定义一个线程类,它)定义一个线程类,它继承类继承类Thread并重写其并重写其中的方法中的方法run( );(2)提供一个)提供一个实现接口实现接口Runnable的类的类作为线程的作为线程的目标对象,在初始化一个目标对象,在初始化一个Thread类或者类或者Thread子子类的线程对象时,把目标对象传递给这个类的线程对象时,把目标对象传递给这个线程实例线程实例,由该由该目标对象提供线程体目标对象提供线程体run( )。Runnable接口中只定义了一个空方法接口中只定义了一个空方法run();(1)通过继承类通过继承类

14、Thread构造线程体构造线程体 class SimpleThread extends Thread public SimpleThread(String str) super(str); public void run() for (int i = 0; i 10; i+) System.out.println(i + +getName(); try sleep(int)(Math.random() * 1000); catch (InterruptedException e) System.out.println(DONE! + getName(); public class TwoThr

15、eadsTest public static void main (String args) new SimpleThread(First).start(); new SimpleThread(Second).start(); 运行结果:运行结果:0 First0 First 0 Second 0 Second 1 Second 1 Second 1 First 1 First 2 First 2 First 2 Second 2 Second 3 Second 3 Second 3 First 3 First 4 First 4 First 4 Second 4 Second 5 First

16、 5 First 5 Second 5 Second 6 Second 6 Second 6 First6 First 7 First 7 First 7 Second 7 Second 8 Second 8 Second 9 Second 9 Second 8 First 8 First DONE! Second DONE! Second 9 First 9 First DONE! FirstDONE! First(2)通过接口构造线程体通过接口构造线程体-例子例子1class SimpleThread implements Runnable public SimpleThread(Stri

17、ng str) /super(str); public void run() for (int i = 0; i 10; i+) System.out.println(i + +Thread.currentThread(). getName(); try Thread.sleep(int)(Math.random() * 1000); catch (InterruptedException e) (2)通过接口构造线程体通过接口构造线程体-例子例子1(续续)System.out.println(DONE! + Thread.currentThread().getName(); public c

18、lass TwoThreadsTest public static void main (String args) new Thread( new SimpleThread(),First). start(); new Thread( new SimpleThread(),“Second). start(); 例子例子1输出结果输出结果0 First0 Second1 Second2 Second1 First3 Second4 Second2 First3 First4 First5 First5 Second6 First6 Second7 First7 Second8 First8 Se

19、cond9 FirstDONE! 9 SecondDONE! (2)通过接口构造线程体通过接口构造线程体-例子例子2public class Clock extends java.applet.Applet implements Runnable Thread clockThread; public void start() if (clockThread = null) clockThread = new Thread(this, Clock); clockThread.start(); public void run() while (clockThread != null) repain

20、t(); try clockThread.sleep(1000); catch (InterruptedException e) public void paint(Graphics g) Date now = new Date(); g.drawString(now.getHours() + : + now.getMinutes() +:+now. getSeconds(), 5, 10); public void stop() clockThread.stop(); clockThread = null; 两种方法的比较两种方法的比较v使用使用Runnable接口接口可以将可以将CPU,代

21、码和数据分开,形成清晰的模型,代码和数据分开,形成清晰的模型;还可以从其他类继承还可以从其他类继承;保持程序风格的一致性;保持程序风格的一致性; 使用使用Thread.currentThread()获取当前线程进行获取当前线程进行控制。控制。v直接继承直接继承Thread类类不能再从其他类继承不能再从其他类继承;编写简单,可以直接操纵线程编写简单,可以直接操纵线程两种构造线程方法之间的关系两种构造线程方法之间的关系线程的调度线程的调度vJava提供一个提供一个线程调度器线程调度器来监控程序中启来监控程序中启动后进入就绪状态的所有线程。线程调度动后进入就绪状态的所有线程。线程调度器按照器按照线程

22、的优先级线程的优先级决定应调度哪些线程决定应调度哪些线程来执行。来执行。vJava运行系统的线程调度算法是抢先式的运行系统的线程调度算法是抢先式的(preemptive)。时间片方式时间片方式非时间片方式非时间片方式v下面几种情况下,当前线程会放弃下面几种情况下,当前线程会放弃CPU:线程调用了线程调用了yield()或或sleep()方法主动放弃或它的方法主动放弃或它的run()方法结束;方法结束;由于当前线程进行由于当前线程进行I/O访问,外存读写,访问,外存读写,等待用户输入等待用户输入等等操作操作,导致线程阻塞;,导致线程阻塞;为等候一个条件变量,线程调用为等候一个条件变量,线程调用w

23、ait()方法;方法;抢先式抢先式系统下,由系统下,由高优先级高优先级的线程参与调度;的线程参与调度;时间片时间片方式下,当前方式下,当前时间片用完时间片用完,由同优先级的线程参,由同优先级的线程参与调度。与调度。注意:使用注意:使用yield()方法只能给同优先级的线程提供执行机方法只能给同优先级的线程提供执行机会,如果没有同优先级的线程处于可运行态,会,如果没有同优先级的线程处于可运行态,yield()将将被忽略。被忽略。线程的优先级线程的优先级v线程的优先级用数字来表示,范围从线程的优先级用数字来表示,范围从1到到10,即即Thread.MIN_PRIORITY(1)到到Thread.M

24、AX_PRIORITY(10)。一个线程的缺。一个线程的缺省优先级是省优先级是5,即,即Thread.NORM_PRIORITY(5)。vint getPriority();vvoid setPriority(int newPriority);class ThreadTest public static void main( String args ) Thread t1 = new MyThread(T1); t1.setPriority( Thread.MIN_PRIORITY ); t1.start( ); Thread t2 = new MyThread(T2); t2.setPrio

25、rity( Thread.MAX_PRIORITY ); t2.start( ); Thread t3 = new MyThread(T3); t3.setPriority( Thread.MAX_PRIORITY ); t3.start( ); class MyThread extends Thread String message; MyThread ( String message ) this.message = message; public void run() for ( int i=0; i3; i+ )System.out.println(message+“ +getPrio

26、rity(); 运行结果:运行结果:T2 10T2 10T2 10T3 10T3 10T3 10T1 1T1 1 T1 1v注意:并不是在所有系统中运行注意:并不是在所有系统中运行Java程程序时都采用序时都采用时间片策略时间片策略调度线程,所以调度线程,所以一个线程一个线程在空闲时应该主动放弃在空闲时应该主动放弃CPU,以使其他同优先级和低优先级的线程得以使其他同优先级和低优先级的线程得到执行。到执行。基本的线程控制基本的线程控制v终止线程终止线程线程执行完其线程执行完其run()方法后,会自然终止。方法后,会自然终止。通过调用线程的实例方法通过调用线程的实例方法stop()来终止线程。来终

27、止线程。v测试线程状态测试线程状态可以通过可以通过Thread中的中的isAlive()方法来获取线方法来获取线程是否处于活动状态;程是否处于活动状态;线程由线程由start()方法启动后,直到其被终止之间方法启动后,直到其被终止之间的任何时刻,都处于的任何时刻,都处于 Alive状态。状态。v线程的暂停和恢复线程的暂停和恢复sleep()方法方法suspend()和和resume()方法方法 join()方法方法当前线程等待调用该方法的线程结束后,再恢复执当前线程等待调用该方法的线程结束后,再恢复执行。行。TimerThread tt=new TimerThread(100);tt.star

28、t();public void timeout()tt.join(); /等待等待线程线程tt执行完后再继续往下执行执行完后再继续往下执行 多线程的互斥与同步多线程的互斥与同步线程线程1线程线程2线程线程10资源资源取过来取过来加加1后送回去后送回去withdrwal()withdrwal()透支透支余额余额变量变量多线程的互斥与同步多线程的互斥与同步v临界资源问题临界资源问题 class stack int idx=0; char data = new char6; public void push(char c) dataidx = c; idx+; public char pop() i

29、dx-; return dataidx; 两个线程两个线程A和和B在同时使用在同时使用Stack的同一个实例对象,的同一个实例对象,A正在往堆栈里正在往堆栈里push一个数据,一个数据,B则要从堆栈中则要从堆栈中pop一个数据。一个数据。1) 操作之前操作之前 data = | p | q | | | | | idx=22) A执行执行push中的第一个语句,将中的第一个语句,将r推入堆栈;推入堆栈; data = | p | q | r | | | | idx=23)A还未执行还未执行idx+语句,语句,A的执行被的执行被B中断,中断,B执执行行pop方法,返回方法,返回q: data =

30、| p | q | r | | | | idx=14)A继续执行继续执行push的第二个语句:的第二个语句: data = | p | q | r | | | | idx=2最后的结果相当于最后的结果相当于r没有入栈。没有入栈。v产生这种问题的原因在于对共享数据访问的操作产生这种问题的原因在于对共享数据访问的操作的的不完整性不完整性。v在在Java 语言中,引入了语言中,引入了对象互斥锁对象互斥锁的概念,来的概念,来保证共享数据操作的完整性。保证共享数据操作的完整性。每个对象都对应于一个可称为每个对象都对应于一个可称为“互斥锁互斥锁”的的标记,这个标记用来保证标记,这个标记用来保证在任一时刻在

31、任一时刻,只能只能有一个线程有一个线程访问该对象。访问该对象。Java运行系统允许一个线程再次取得已为自运行系统允许一个线程再次取得已为自己控制的对象锁,即对象锁是可重入的。己控制的对象锁,即对象锁是可重入的。 关键字关键字synchronized 来来与对象的互斥锁联与对象的互斥锁联系系。当某个对象用。当某个对象用synchronized修饰时,表修饰时,表明该对象在任一时刻只能由一个线程访问明该对象在任一时刻只能由一个线程访问。 public void push(char c) /synchronized实例方法是使用实例方法是使用this锁去做线程的锁去做线程的 /共享互斥共享互斥 sy

32、nchronized(this) dataidx=c; idx+; public char pop() synchronized(this) idx-; return dataidx; vsynchronized 除了象上面讲的放在除了象上面讲的放在对象前对象前面面限制一段代码的执行外,还可以放在限制一段代码的执行外,还可以放在方方法声明法声明中,表示整个方法为同步方法。方中,表示整个方法为同步方法。方法在执行时需要获取法在执行时需要获取“互斥锁互斥锁”,是一个,是一个原子方法,不会被外界中断原子方法,不会被外界中断 。public synchronized void push(char c)

33、v如果如果synchronized用在类声明中,则表明用在类声明中,则表明该类中的该类中的所有方法所有方法都是都是synchronized的。的。多线程的同步多线程的同步 /同步化的堆栈类,定义了两个同步方法同步化的堆栈类,定义了两个同步方法 class SyncStackprivate int index = 0;private char buffer = new char6;public synchronized void push(char c) while(index = buffer.length) try /或或wait(); 等待消费者取走数据等待消费者取走数据t h i s .

34、 w a i t ( ) ; catch(InterruptedException e) /最好使用最好使用notifyAll(),通知消费者可以来取,通知消费者可以来取/数据数据 this.notify(); bufferindex = c; index+;public synchronized char pop() while(index =0)try /或或wait(); 等待生产者生产数据等待生产者生产数据this.wait(); catch(InterruptedException e) /最好使用最好使用notifyAll(),通知生产者可以再次写入,通知生产者可以再次写入/数据数

35、据 this.notify(); index- -; return bufferindex; v注意:方法注意:方法notifyAll()唤醒的是由于等待唤醒的是由于等待notifyAll()方法所在对象方法所在对象(这里是这里是SyncStack)的所有线程,被唤醒的线程会去的所有线程,被唤醒的线程会去竞争对象锁竞争对象锁,当其中某个线程得到锁之后,其他的线程重新进入阻塞状态。而当其中某个线程得到锁之后,其他的线程重新进入阻塞状态。而notify()方法是不安全的,因为你方法是不安全的,因为你无法控制让哪一个线程离开阻塞队无法控制让哪一个线程离开阻塞队列列。如果让一个不合适的线程离开等待队列

36、,它也可能仍无法向前。如果让一个不合适的线程离开等待队列,它也可能仍无法向前运行。因此建议使用运行。因此建议使用notifyAll()方法,让方法,让等待队列等待队列上的上的所有和当前所有和当前对象相关的线程对象相关的线程离开阻塞状态。离开阻塞状态。生产者生产者-消费者问题消费者问题 /生产者线程生产者线程 class Producer implements RunnableSyncStack theStack;public Producer(SyncStack s)theStack = s; 资源资源SyncStack生产者生产者消费者消费者public void run()char c;f

37、or(int i=0; i20; i+)c =(char)(Math.random()*26+A);theStack.push(c);System.out.println(Produced: +c);try /当前线程中断执行当前线程中断执行run()方法,进入方法,进入 /阻塞状态,给其他线程以执行的机会阻塞状态,给其他线程以执行的机会Thread.sleep(int)(Math.random()*100);catch(InterruptedException e) /消费者线程消费者线程 class Consumer implements RunnableSyncStack theStac

38、k;public Consumer(SyncStack s)theStack = s;public void run() char c; for(int i=0;i20;i+) c = theStack.pop(); System.out.println(Consumed: +c);try Thread.sleep(int)(Math.random()*1000); catch(InterruptedException e) /模拟生产者模拟生产者-消费者问题的主程序消费者问题的主程序 public class SyncTestpublic static void main(String ar

39、gs)SyncStack stack = new SyncStack();Runnable source=new Producer(stack);Runnable sink = new Consumer(stack);Thread t1 = new Thread(source);Thread t2 = new Thread(sink);t1.start();t2.start();程序执行结果程序执行结果Produced:VConsumed:VProduced:EConsumed:EProduced:PProduced:L.Consumed:LConsumed:Pwait(), notify(), notifyAll()(1) wait,nofity,notifyAll必须在必须在已经持有锁已经持有锁的情况的情况下执行,所以它们只能出现在下执行,所以它们只能出现在synchronized作用的作用的范围内。范围内。 这些方法都是在这些方法都是在java.lang.Object中定义的。中定义的。(2) wait的作用:释放已持有的锁,进入的作用:释放已持有的锁,进入wait队列。队列。(3) notify的作用:唤醒的作用:唤醒wait队列中的一个线程队列中的一个线程,并把,并把它移入锁申请队列。它移入锁申请队列。(4)

温馨提示

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

评论

0/150

提交评论