Java08-Java高级编程2-多线程.pptx
第第 9 9 讲讲JavaJava的多线程机制的多线程机制 一、程序、进程与线程一、程序、进程与线程二、线程的状态二、线程的状态三、线程优先级与调度三、线程优先级与调度四、多线程实现机制四、多线程实现机制五、基本的线程控制五、基本的线程控制 多多线线程程是是指指同同时时存存在在几几个个执执行行体体,按按几几条条不不同同的的执执行路线共同工作的情况。行路线共同工作的情况。Java内置对多线程的支持内置对多线程的支持。Copyright 2011 Keerqin All Rights Reserved.一、程序、进程与线程一、程序、进程与线程程序:一段静态的代码。进进程程:是程序的一次动态执行过程,包括从代码加载、执行到执行完毕的一个完整的产生、发展到消亡的过程。线线程程:是比进程更小的执行单位,一个进程在执行过程中,可以产生多个线程。即每个线程都是一个动态的产生、存在和消亡的过程。Copyright 2011 Keerqin All Rights Reserved.多线程的优势多线程的优势减轻编写交互频繁、涉及面多的程序的困减轻编写交互频繁、涉及面多的程序的困难难.程序的吞吐量会得到改善程序的吞吐量会得到改善.有多个处理器的系统有多个处理器的系统,可以并发运行不同的可以并发运行不同的线程线程.(否则否则,任何时刻只有一个线程在运行任何时刻只有一个线程在运行)Copyright 2011 Keerqin All Rights Reserved.线程与进程的区别线程与进程的区别多个进程的内部数据和状态都是完全独立的多个进程的内部数据和状态都是完全独立的,而而多线程是共享一块内存空间和一组系统资源多线程是共享一块内存空间和一组系统资源,有有可能互相影响可能互相影响.线程本身的数据通常只有寄存器数据,以及一个线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。切换的负担要小。多线程基本概念多线程基本概念文文件件输入输出装置输入输出装置各种系统资源各种系统资源数据区段数据区段程序区段程序区段只有一个地方在执行只有一个地方在执行文件文件输入输出装置输入输出装置各种系统资源各种系统资源数据区段数据区段程序区段程序区段同时有数个地方在执行同时有数个地方在执行传统的进程传统的进程多线程的任务多线程的任务Copyright 2011 Keerqin All Rights Reserved.二、线程的状态二、线程的状态stop()或或 run()结束结束New ThreadNew Thread创建状态创建状态Not RunnableNot Runnable不可运行状态不可运行状态DeadDead消亡状态消亡状态new Thread()yield()start()stop()stop()RunnableRunnable运行状态运行状态sleep()suspend()wait()resume()notify()notifyAll()Copyright 2011 Keerqin All Rights Reserved.创建状态(new Thread)由主线程创建线程对象由主线程创建线程对象 Thread myThread=new MyThreadClass();可运行状态(Runnable)Thread myThread=new MyThreadClass();myThread.start();满足一定的调度条件时,进入满足一定的调度条件时,进入Runnable状态。状态。Copyright 2011 Keerqin All Rights Reserved.不可运行状态(Not Runnable)调用了调用了sleep()方法()方法;调用了调用了suspend()方法()方法;为等候一个条件变量,线程调用为等候一个条件变量,线程调用wait()方法()方法;输入输出流中发生线程阻塞输入输出流中发生线程阻塞;死亡状态(Dead)线程的终止一般可通过两种方法实现:自然撤消线程的终止一般可通过两种方法实现:自然撤消(线程执行完)或是被停止(调用(线程执行完)或是被停止(调用stop()方法)。方法)。Copyright 2011 Keerqin All Rights Reserved.三、线程优先级与调度三、线程优先级与调度优先级:优先级:确保重要的或时间紧迫的线程即时运行方法。调度:调度:强制优先与公平的方法。线程调度器:线程调度器:按照线程的优先级决定应调度哪些线程来执行,具有高优先级的线程会在较低优先级的线程之前得到执行。线程的调度是抢先式的,如果在当前线程的执行过程中,一个具有更高优先级的线程进入就绪状态,则这个高优先级的线程立即被调度执行。对优先级相同的线程来说,调度将采用轮转法。Copyright 2011 Keerqin All Rights Reserved.线程优先级与调度线程优先级与调度线程优先级用数字来表示,范围从1到10,即Thread.MIN_PRIORITY到Thread.MAX_PRIORITY。一个线程的缺省优先级是5,即Thread.NORM_PRIORITY。我们可以通过下面方法获得或改变线程的优先级。int getPriority();void setPriority(int newPriority);Copyright 2011 Keerqin All Rights Reserved.四、多线程实现机制四、多线程实现机制Java的线程:通过类来实现。每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run()称为线程体。构造线程体的2种方法:定义一个线程类,它继承类Thread并重写其中的方法run();提供一个实现接口Runnable的类作为线程的目标对象,在初始化一个Thread类或者Thread子类的线程对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。Copyright 2011 Keerqin All Rights Reserved.创建多线程的方式1、Thread class2、Runnable interfaceCopyright 2011 Keerqin All Rights Reserved.1.public class mythread extends Applet implements Runnable (小应用或已经是某个类的子类时小应用或已经是某个类的子类时)2.继承类继承类Thread public class mythread extends Thread3.上述两种方法中都可用类上述两种方法中都可用类Thread产生线程的产生线程的对象对象 Thread newthread;4.创建并启动线程创建并启动线程 newthread=new Thread(this);newthread.start();Copyright 2011 Keerqin All Rights Reserved.5.run方法是运行线程的主体方法是运行线程的主体,启动线程时启动线程时,由由java直接调用直接调用 public void run()6.停止线程停止线程,由小应用程序的由小应用程序的stop调用线程的调用线程的stop newthread.stop()7 sleep方法的作用方法的作用,暂停线程的执行暂停线程的执行,让其它线程让其它线程得到机会得到机会,sleep要丢出异常要丢出异常,必须抓住必须抓住.Trysleep(100)catch(InterruptedException e)例例:小应用程序中不用小应用程序中不用Runnable接口仍然可以使接口仍然可以使用线程用线程(不调用主类的方法和调用主类的方法不调用主类的方法和调用主类的方法)import java.applet.*;public class thread extends Applet mythread t1=new mythread();public start()t1.start();class mythread extends Thread public void run()for(int i=0;i4;i+)System.out.println(“”+i);trysleep(400);catch(InteruptedException e)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();trysleep(400);catch(InterruptedException e)Copyright 2011 Keerqin All Rights Reserved.isAlive:判断线程目前是否正在执行状态中判断线程目前是否正在执行状态中 if(newthread.isAlive()newthread.stop();resume:要求被暂停得线程继续执行要求被暂停得线程继续执行 suspend:暂停线程的执行暂停线程的执行 join:等待线程执行完毕等待线程执行完毕 thatThread.join();被等待的那个线程不结束被等待的那个线程不结束,当前线当前线程就一直等待程就一直等待.yield:将执行的权力交给其它线程将执行的权力交给其它线程,自己到队列的最自己到队列的最后等待后等待.其它常用的方法Copyright 2011 Keerqin All Rights Reserved.某一时刻只有一个线程在执行某一时刻只有一个线程在执行,调度策略为固定优调度策略为固定优先级调度先级调度.newthread.setPriority(Thread.MIN_PRIORITY)级别有级别有:MIN-PRIORITY NOM_PRIORITY MAX-PRIORITY自私的线程自私的线程:有很高的优先权的线程有很高的优先权的线程,不主动睡眠或不主动睡眠或让出处理器控制权让出处理器控制权.线程的优先权线程的优先权Copyright 2011 Keerqin All Rights Reserved.当一个线程执行完所有语句后就自动终止,当一个线程执行完所有语句后就自动终止,调用线程的调用线程的stop()方法,也可以强制终止线方法,也可以强制终止线程。程。如果希望线程正常终止,可采用标记来使线如果希望线程正常终止,可采用标记来使线程中的程中的run()方法退出。()方法退出。public class Xyz implements Runnable private boolean timeToQuit=false;public void run()()while(!(!timeToQuit)./clean up before run()()ends;public void stopRunning()timeToQuit=true;public class ControlThread private Runnable r=new Xyz();private Thread t=new Thread(r);public void startThread()t.start();publi void stopThread()r.stopRunning();Copyright 2011 Keerqin All Rights Reserved.暂停线程的执行等待条件满足再执行暂停线程的执行等待条件满足再执行.下面的例子显示线程的挂起和唤醒下面的例子显示线程的挂起和唤醒小应用程序第一次开始时小应用程序第一次开始时,线程被启动线程被启动浏览器改变页面时浏览器改变页面时,小应用程序的小应用程序的stop()方法方法被调用被调用,线程被挂起线程被挂起.浏览器回到原来的页面时浏览器回到原来的页面时,线程被唤醒线程被唤醒.Copyright 2011 Keerqin All Rights Reserved.public void start()if(mythread=null)mythread=new Thread();mythread.start();else mythread.resume();public void run()while(true)trysleep(100);catch(InterruptedException e)public void stop()mythread.suspend();.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.random()*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();2.通过接口构造线程体通过接口构造线程体import java.applet.*;import java.util.*;import java.awt.*;public class Clock extends Applet implements Runnable Thread clockThread;public void start()if(clockThread=null)clockThread=new Thread(this,ClockTime);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();clockThread=null;Copyright 2011 Keerqin All Rights Reserved.两种方法的比较两种方法的比较使用Runnable接口可以将可以将CPU,代码和数据分开,形成清晰的模型,代码和数据分开,形成清晰的模型;还可以从其他类继承还可以从其他类继承;保持程序风格的一致性。保持程序风格的一致性。直接继承Thread类不能再从其他类继承不能再从其他类继承;编写简单,可以直接操纵线程,无需使用编写简单,可以直接操纵线程,无需使用Thread.currentThread()。Copyright 2011 Keerqin All Rights Reserved.五、基本的线程控制五、基本的线程控制终止线程终止线程线程执行完其run()方法后,会自然终止。通过调用线程的实例方法stop()来终止线程。测试线程状态测试线程状态可以通过Thread中的isAlive()方法来获取线程是否处于活动状态;线程由start()方法启动后,直到其被终止之间的任何时刻,都处于 Alive状态。线程的暂停和恢复线程的暂停和恢复sleep(timeout)方法suspend()和resume()可以由线程自身调用suspend()方法暂停自己,也可以由其它线程调用suspend()方法暂停其执行,但是要恢复由suspend()方法挂起的线程,只能由其它线程来调用resume()方法。join()挂起当前线程。挂起的时间或者为timeout毫秒,或者挂起当前线程直至它所调用的线程终止。Copyright 2011 Keerqin All Rights Reserved.多线程问题-执行的顺序多个线程运行时多个线程运行时,调度策略为固定优先级调度调度策略为固定优先级调度.级别相同级别相同时时,由操作系统按时间片来分配由操作系统按时间片来分配下面给出的例子中下面给出的例子中,共运行三个线程共运行三个线程,它们做同样的事它们做同样的事,每每次打印循环次数和自己的序列号次打印循环次数和自己的序列号,运行结果表明运行结果表明,它们并它们并不是连续运行的不是连续运行的.在上例中如果给某个线程赋予较高的优先权在上例中如果给某个线程赋予较高的优先权,则发现这则发现这个进程垄断控制权个进程垄断控制权 thread.setPriority(Thread.MAX_PRIORITY)Copyright 2011 Keerqin All Rights Reserved.7.3 多线程问题多线程问题/多个进程运行时执行顺序是交叉的多个进程运行时执行顺序是交叉的class multithread extends Thread int threadNum;public static void main(String args)multithread array=new multithread3;for(int i=0;i3;i+)arrayi=new multithread(i);for(int i=0;i3;i+)arrayi.start();multithread(int SerialNum)super();threadNum=SerialNum;public void run()for(int j=0;j5;j+)System.out.println(“+MySerialNum);System.out.println(thread+threadNum+bye.);Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-如何写多线程如何写多线程1.1.分别定义不同的线程类分别定义不同的线程类,在各自的在各自的runrun方法中定义线程的工作方法中定义线程的工作 class mythread1 extends Thread public void run.class mythread2 extends Thread public void run.2.在主类中实例化各线程类在主类中实例化各线程类,并启动线程并启动线程.public class demo extends Applet public void init()mythread t1=new mythread1();mythread t2=new mythread2();t1.start();t2.start();Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-线程间的通信线程间的通信1.线程间的通信可以用管道流线程间的通信可以用管道流,2.创建管道流创建管道流:PipedInputStream pis=new PipedInputStream();PipedOutputStream pos=new PipedOutputStream(pis);或或:PipedOutputStream pos=new PipedOutputStream();PipedInputStream pis=new PipedInputStream(pos);线程线程1PipedOutputStream PipedInputStream输出流输出流outStream输入流输入流inStream线程线程2多线程问题多线程问题-线程间的通信线程间的通信管道流不能直接读写管道流不能直接读写PrintStream p=new PrintStream(pos);p.println(“hello”);DataInputStream d=new DataInputStream(pis);d.readLine();2.通过一个中间类来传递信息通过一个中间类来传递信息.线程线程2线程线程1中间类中间类mssm.write(s)s=m.read()write()read()printStream DataInputStreamCopyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-线程间的通信线程间的通信管道流可以连接两个线程间的通信管道流可以连接两个线程间的通信下面的例子里有两个线程在运行下面的例子里有两个线程在运行,一个往外输出信息一个往外输出信息,一个读入信息一个读入信息.将一个写线程的输出通过管道流定义为读线程的输入将一个写线程的输出通过管道流定义为读线程的输入.outStream=new PipedOutputStream();inStream=new PipedInputStream(outStream);new Writer(outStream).start();new Reader(inStream).start();Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-线程间的通信线程间的通信主类主类Pipethread辅类辅类Writer线线程程类类辅类辅类Reader线线程程类类管管道道流流将数据写将数据写到输出流到输出流从流中读数据从流中读数据输入流输入流作为参数传给作为参数传给WriterWriter(outStream)Copyright 2011 Keerqin All Rights Reserved.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)Copyright 2011 Keerqin All Rights Reserved.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)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)Copyright 2011 Keerqin All Rights Reserved.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:+messagesi);p.close();p=null;Copyright 2011 Keerqin All Rights Reserved.多线程问题-资源协调1.数据的完整性数据的完整性线程线程1线程线程2线程线程10资源资源取过来取过来加加1后送回去后送回去withdrwal()withdrwal()透支透支余额余额变量变量Copyright 2011 Keerqin All Rights Reserved.多线程问题-资源协调对共享对象的访问必须同步对共享对象的访问必须同步,叫做条件变量叫做条件变量.Java语言允许通过监视器语言允许通过监视器(有的参考书称其为管程有的参考书称其为管程)使用条件变量实现线程使用条件变量实现线程同步同步.监视器阻止两个线程同时访问同一个条件变量监视器阻止两个线程同时访问同一个条件变量.它的如同锁一样作用在数它的如同锁一样作用在数据上据上.线程线程1进入进入withdrawal方法时方法时,获得监视器获得监视器(加锁加锁);当线程当线程1的方法执行完毕的方法执行完毕返回时返回时,释放监视器释放监视器(开锁开锁),线程线程2的的withdrawal方能进入方能进入.withdrawal()线程线程1监视器监视器线程线程2Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-资源协调资源协调用用synchronized来标识的区域或方法即为监视来标识的区域或方法即为监视器监视的部分。器监视的部分。一个类或一个对象由一个监视器一个类或一个对象由一个监视器,如果一个程序如果一个程序内有两个方法使用内有两个方法使用synchronized标志标志,则他们在则他们在一个监视器管理之下一个监视器管理之下.一般情况下,只在方法的层次上使用关键区一般情况下,只在方法的层次上使用关键区readwrite监监视视器器线程线程1线程线程2Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-资源协调资源协调此处给出的例子演示两个线程在同步限制下工作的情况此处给出的例子演示两个线程在同步限制下工作的情况.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);Copyright 2011 Keerqin All Rights Reserved.多线程问题-资源协调2.等待同步数据等待同步数据生产者生产者消费者消费者.共享对象共享对象writeread可能出现的问题可能出现的问题:生产者比消费者快时生产者比消费者快时,消费者会漏掉一些数据没有取到消费者会漏掉一些数据没有取到消费者比生产者快时消费者比生产者快时,消费者取相同的数据消费者取相同的数据.notify()和和wait()方法用来协调读取的关系方法用来协调读取的关系.notify()和和wait()都只能从同步方法中的调用都只能从同步方法中的调用.Copyright 2011 Keerqin All Rights Reserved.多线程问题-资源协调notify的作用是唤醒正在等待同一个监视器的的作用是唤醒正在等待同一个监视器的线程线程.wait的作用是让当前线程等待的作用是让当前线程等待信息版例子信息版例子read()方法在读信息之前先等待方法在读信息之前先等待,直到信息可读直到信息可读,读完后通知要写的线程读完后通知要写的线程.write()方法在写信息之前先等待方法在写信息之前先等待,直到信息被取直到信息被取走走,写完后通知要读的进程写完后通知要读的进程.Copyright 2011 Keerqin All Rights Reserved.多线程问题-资源协调多线程问题多线程问题-资源的协调和锁定资源的协调和锁定死锁问题死锁问题如果你的持有一个锁并试图获取另一个锁时如果你的持有一个锁并试图获取另一个锁时,就有死锁的危就有死锁的危险险.解决死锁问题的方法解决死锁问题的方法:给条件变量施加排序给条件变量施加排序线程线程2pen线程线程1note把把“pen”给我给我,我我才能给你才能给你“note”把把“note”给我给我,我我才能给你才能给你“pen”Copyright 2011 Keerqin All Rights Reserved.多线程问题多线程问题-daemon线程线程什么是什么是daemon(守护守护)?在客户在客户/服务器模式下服务器模式下,服务器的作用是等待用户发来请求服务器的作用是等待用户发来请求,并并按请求完成客户的工作按请求完成客户的工作守护线程是为其它线程提供服务的线程守护线程是为其它线程提供服务的线程守护线程一般应该是一个独立的线程守护线程一般应该是一个独立的线程,它的它的run()方法是一个无方法是一个无限循环限循环.守护线程与其它线程的区别是守护线程与其它线程的区别是,如果守护线程是唯一运行着的如果守护线程是唯一运行着的线程线程,程序会自动退出程序会自动退出客户端 服务器端requestdaemonCopyright 2011 Keerqin All Rights Reserved.小结小结1.实现线程有两种方法实现线程有两种方法:实现实现Ruannable接口接口继承继承Thread类类2.在小应用中通常在在小应用中通常在start中创建线程中创建线程3.当新线程被启动时当新线程被启动时,java调用该线程的调用该线程的run方方 法法,它是它是Thread的核心的核心.4.线程由四个状态线程由四个状态:新生新生,运行运行,暂停暂停,死亡死亡5.线程间的通信方式由三种线程间的通信方式由三种:完全共享数据完全共享数据,通过监视器通过监视器,通过通过join.Copyright 2011 Keerqin All Rights Reserved.小结小结6.两个或多个线程竞争资源时两个或多个线程竞争资源时,需要用同步的需要用同步的方法协调资源方法协调资源.7.多个线程执行时多个线程执行时,要用到同步方法要用到同步方法,即使用即使用 synchronized的关键字设定同步区的关键字设定同步区8.wait和和notify起协调作用起协调作用9.守护进程的特点是当程序中制胜它自己时守护进程的特点是当程序中制胜它自己时,会自动中止会自动中止.