《javake课件第8章Java线程.ppt》由会员分享,可在线阅读,更多相关《javake课件第8章Java线程.ppt(60页珍藏版)》请在三一办公上搜索。
1、第8章 Java线程,学习导读 本章将介绍Java线程编程技术,以及多线程的同步和互斥。,第8章 Java线程,线程基础线程的生命多线程共享数据多线程的互斥和同步本章小结,线程的概念,多任务:计算机在看上去几乎同一时间内运行多个程序。多线程:单个程序内部也可以在同一时间进行多种运算。一个线程是一个程序内部的顺序控制流。不是程序,自己本身不能运行,必须在程序中运行。如何在一个程序内部实现多个线程。,线程和进程每个进程都有独立的代码和数据空间(进程上下文),进程切换的开销大。线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。多进程:在操作
2、系统中,能同时运行多个任务(程序)。多线程:在同一应用程序中,有多个顺序流同时执行。,线程的概念模型,虚拟的CPU,封装在类中。CPU所执行的代码,传递给Thread类。CPU所处理的数据,传递给Thread类。,线程体,类来实现的。当我们生成一个Thread类或者它的子类的对象后,一个新的线程就诞生了。每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run()称为线程体。,线程的状态,唤醒,挂起,睡眠或等待,创建状态(new Thread)Thread myThread=new MyThreadClass();可运行状态(Runnable)/非运行态Thread
3、myThread=new MyThreadClass();myThread.start();这一状态并不是运行中状态(Running),因为也许线程并未真正执行,只有被调度执行时,才真正处于运行状态。,不可运行状态(Not Runnable,Blocked)调用了sleep()方法;/此时线程的run()方法将中断执行调用了suspend()方法;/挂起线程,不推荐使用为等候一个条件变量,线程调用wait()方法;I/O处发生线程阻塞;/I/O阻塞对于上面四种情况,都有可以返回可运行态的方法与之对应。1.sleep()方法中的参数为休息时间,单位为毫秒,时间过去后,线程即为可运行的。/不是运行
4、态,需再次被调度2.一个线程调用suspend()方法后,只能有其它线程调用它的resume()方法恢复。3.如果一个线程等待条件变量,如果要停止等待,需要条件变量所在的对象调用notify()或者 notifyAll()方法。4.特定的I/O指令完成,结束不可运行状态。,死亡状态(Dead)线程的终止一般可通过两种方法实现:自然撤销(线程执行完)或是被停止(调用stop()方法,不推荐使用)。自然撤销是线程的 run()方法正常退出。如下面的程序:public void run()int i=0;for(i=0;i10;i+)System.out.println(i);t.start();/
5、t 是一个线程./省略了一些语句 t.stop();/调用 stop()终止线程,其它注意事项1.非法状态处理 对于任何状态,如果调用的方法和状态不符,都会引起非法状态处理。例如,线程刚创建后,只能调用 start()或者 stop()方法,如果调用其它方法就会引起非法状态处理。2.isAlive()方法 在类Thread中提供了方法isAlive(),如果线程已经启动(start),但是未终止(dead),返回true,反之,返回 false,表示该线程未启动,或者已终止。如果isAlive()方法返回true,不能区分是可运行态(runnable)还是不可运行态(not runnable)
6、。,顺序执行(单线程)程序,public class SequentialDemopublic static void main(String args)new Sequential(“C).run();new Sequential(“D).run();class SequentialString 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(InterruptedExc
7、eption e)e.printStackTrace();System.out.print(name);,每次总输出结果:CCCCCDDDDD,线程体的构造,(1)public Thread();/创建一个线程对象(2)public Thread(Runnable target);/参数target的run()方法将被线程对象调用(3)public Thread(String name);/线程的名称(4)public Thread(Runnable target,String name);(5)public Thread(ThreadGroup group,Runnable target);
8、(6)public Thread(ThreadGroup group,String name);(7)public Thread(ThreadGroup group,Runnable target,String name);group指明线程所在的组,可以把很多线程加在一个组里面,进行集中控制。target是执行线程体的目标对象。生成这个对象的类,实现了Runnable接口。其中包含线程体run()方法。name是线程的名称。,任何实现接口Runnable的对象都可以作为一个线程的目标对象;构造线程体的两种方法(1)定义一个线程类,它继承类Thread并重写其中的方法run();(2)提供一个
9、实现接口Runnable的类作为线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。Runnable接口中只定义了一个空方法run();,(1)通过继承类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.ran
10、dom()*1000);catch(InterruptedException e),System.out.println(DONE!+getName();public class TwoThreadsTest public static void main(String args)new SimpleThread(First).start();new SimpleThread(Second).start();,运行结果:0 First 0 Second 1 Second 1 First 2 First 2 Second 3 Second 3 First 4 First 4 Second 5 F
11、irst 5 Second 6 Second,6 First 7 First 7 Second 8 Second 9 Second 8 First DONE!Second 9 First DONE!First,(2)通过接口构造线程体-例子1,class SimpleThread implements Runnable public SimpleThread(String str)/super(str);public void run()for(int i=0;i 10;i+)System.out.println(i+Thread.currentThread().getName();try T
12、hread.sleep(int)(Math.random()*1000);catch(InterruptedException e),(2)通过接口构造线程体-例子1(续),System.out.println(DONE!+Thread.currentThread().getName();public class TwoThreadsTest public static void main(String args)new Thread(new SimpleThread(),First).start();new Thread(new SimpleThread(),“Second).start()
13、;,例子1输出结果,0 First0 Second1 Second2 Second1 First3 Second4 Second2 First3 First4 First,5 First5 Second6 First6 Second7 First7 Second8 First8 Second9 FirstDONE!9 SecondDONE!,(2)通过接口构造线程体-例子2,implements Runnable Thread clockThread;public void start()if(clockThread=null)clockThread=new Thread(this,Clock
14、);clockThread.start();,public void run()while(clockThread!=null)repaint();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();clockThrea
15、d=null;,两种方法的比较,使用Runnable接口可以将CPU,代码和数据分开,形成清晰的模型;还可以从其他类继承;保持程序风格的一致性;使用Thread.currentThread()获取当前线程进行控制。直接继承Thread类不能再从其他类继承;编写简单,可以直接操纵线程,两种构造线程方法之间的关系,线程的调度,Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行。Java运行系统的线程调度算法是抢先式的(preemptive)。时间片方式非时间片方式,下面几种情况下,当前线程会放弃CPU:线程调用了yield()或s
16、leep()方法主动放弃或它的run()方法结束;由于当前线程进行I/O访问,外存读写,等待用户输入等操作,导致线程阻塞;为等候一个条件变量,线程调用wait()方法;抢先式系统下,由高优先级的线程参与调度;时间片方式下,当前时间片用完,由同优先级的线程参与调度。注意:使用yield()方法只能给同优先级的线程提供执行机会,如果没有同优先级的线程处于可运行态,yield()将被忽略。,线程的优先级,线程的优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10)。一个线程的缺省优先级是5,即Thread.NORM_PRIO
17、RITY(5)。int getPriority();void 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.setPriority(Thread.MAX_PRIORITY);t2.start();Thread t3=new MyThread(T3);t3.setPrior
18、ity(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+“+getPriority();,运行结果:T2 10T2 10T2 10T3 10T3 10T3 10T1 1T1 1 T1 1,注意:并不是在所有系统中运行Java程序时都采用时间片策略调度线程,所以一个线程在空闲时应
19、该主动放弃CPU,以使其他同优先级和低优先级的线程得到执行。,基本的线程控制,终止线程线程执行完其run()方法后,会自然终止。通过调用线程的实例方法stop()来终止线程。测试线程状态可以通过Thread中的isAlive()方法来获取线程是否处于活动状态;线程由start()方法启动后,直到其被终止之间的任何时刻,都处于 Alive状态。,线程的暂停和恢复sleep()方法suspend()和resume()方法 join()方法当前线程等待调用该方法的线程结束后,再恢复执行。TimerThread tt=new TimerThread(100);tt.start();public voi
20、d timeout()tt.join();/等待线程tt执行完后再继续往下执行,多线程的互斥与同步,多线程的互斥与同步,临界资源问题 class stack int idx=0;char data=new char6;public void push(char c)dataidx=c;idx+;,public char pop()idx-;return dataidx;两个线程A和B在同时使用Stack的同一个实例对象,A正在往堆栈里push一个数据,B则要从堆栈中pop一个数据。1)操作之前 data=|p|q|idx=22)A执行push中的第一个语句,将r推入堆栈;data=|p|q|r
21、|idx=2,3)A还未执行idx+语句,A的执行被B中断,B执行pop方法,返回q:data=|p|q|r|idx=14)A继续执行push的第二个语句:data=|p|q|r|idx=2最后的结果相当于r没有入栈。产生这种问题的原因在于对共享数据访问的操作的不完整性。,在Java 语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。Java运行系统允许一个线程再次取得已为自己控制的对象锁,即对象锁是可重入的。关键字synchronized 来与对象的互斥锁联系。当某个对象用synchr
22、onized修饰时,表明该对象在任一时刻只能由一个线程访问。,public void push(char c)/synchronized实例方法是使用this锁去做线程的/共享互斥 synchronized(this)dataidx=c;idx+;public char pop()synchronized(this)idx-;return dataidx;,synchronized 除了象上面讲的放在对象前面限制一段代码的执行外,还可以放在方法声明中,表示整个方法为同步方法。方法在执行时需要获取“互斥锁”,是一个原子方法,不会被外界中断。public synchronized void pus
23、h(char c)如果synchronized用在类声明中,则表明该类中的所有方法都是synchronized的。,多线程的同步,/同步化的堆栈类,定义了两个同步方法 class SyncStackprivate int index=0;private char buffer=new char6;public synchronized void push(char c)while(index=buffer.length)try/或wait();等待消费者取走数据this.wait();catch(InterruptedException e),/最好使用notifyAll(),通知消费者可以来
24、取/数据 this.notify();bufferindex=c;index+;public synchronized char pop()while(index=0)try/或wait();等待生产者生产数据this.wait();catch(InterruptedException e),/最好使用notifyAll(),通知生产者可以再次写入/数据 this.notify();index-;return bufferindex;注意:方法notifyAll()唤醒的是由于等待notifyAll()方法所在对象(这里是SyncStack)的所有线程,被唤醒的线程会去竞争对象锁,当其中某个线
25、程得到锁之后,其他的线程重新进入阻塞状态。而notify()方法是不安全的,因为你无法控制让哪一个线程离开阻塞队列。如果让一个不合适的线程离开等待队列,它也可能仍无法向前运行。因此建议使用notifyAll()方法,让等待队列上的所有和当前对象相关的线程离开阻塞状态。,生产者-消费者问题,/生产者线程 class Producer implements RunnableSyncStack theStack;public Producer(SyncStack s)theStack=s;,资源SyncStack,生产者,消费者,public void run()char c;for(int i=0
26、;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 theStack;public Consumer(SyncStack s)theStack=s;,public v
27、oid 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 args)SyncStack stack=new SyncStack();Runnable source=new Producer(stack);Ru
28、nnable 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:P,wait(),notify(),notifyAll(),(1)wait,nofity,notifyAll必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用的范围内。这些方法都
29、是在中定义的。(2)wait的作用:释放已持有的锁,进入wait队列。(3)notify的作用:唤醒wait队列中的一个线程,并把它移入锁申请队列。(4)notifyAll的作用:唤醒wait队列中的所有的线程并把它们移入锁申请队列.,模拟考题,Question 1)Which of the following are methods of the Runnable interface1)run 2)start 3)yield 4)stop,模拟考题,Answer to Question 1)1)The Runnable interface has only one method run th
30、at needs to be created in any class that implements it.The start method is used to actually call and start the run method executing.,模拟考题,Question 2)What will happen when you attempt to compile and run the following codepublic class Borley extends Threadpublic static void main(String argv)Borley b=n
31、ew Borley();b.start();public void run()System.out.println(Running);,模拟考题,1)Compilation and run but no output 2)Compilation and run with the output Running 3)Compile time error with complaint of no Thread target 4)Compile time error with complaint of no access to Thread package,模拟考题,Answer to Questio
32、n 2)2)Compilation and run with the output Running,模拟考题,Question 3)Given a reference called t to a class which extends Thread,which of the following will cause it to give up cycles to allow another thread to execute.1)t.yield();2)yield();3)yield(100);/or some other suitable amount in milliseconds4)yield(t);,模拟考题,Answer to Question 3)1)t.yield();2)yield(),本章小结,本章主要介绍了Java线程的编程技术。介绍了多线程的同步和互斥,以及应用举例。,