第11章 多线程ppt课件.ppt
第11章 多线程第第1111章章 多线程多线程JavaJava程序设计基础程序设计基础主要内容:主要内容:p 多线程的概念多线程的概念p 实现多线程的两种方法实现多线程的两种方法p 线程的生命周期和线程控制线程的生命周期和线程控制p 线程的同步线程的同步程序是一段静态的代码。程序是一段静态的代码。进程是程序的一次动态执行过程,对应有代码加载、进程是程序的一次动态执行过程,对应有代码加载、执行到执行完毕的完整过程。执行到执行完毕的完整过程。线程是比进程更小的执行单位,是在一个进程中独立线程是比进程更小的执行单位,是在一个进程中独立的控制流,即程序内部的控制流。的控制流,即程序内部的控制流。一个进程在其执行过程中,可以产生多个线程一个进程在其执行过程中,可以产生多个线程多多线程,形成多条执行线索。线程,形成多条执行线索。线程本身不能自动运行,栖身于某个进程之中,由进线程本身不能自动运行,栖身于某个进程之中,由进程启动执行。程启动执行。Java多线程的运行与平台相关。多线程的运行与平台相关。对于单处理器系统,多个线程分时间片获取对于单处理器系统,多个线程分时间片获取CPU或其或其他系统资源来运行。他系统资源来运行。对于多处理器系统,线程可以分配到多个处理器中,对于多处理器系统,线程可以分配到多个处理器中,从而真正的并发执行多任务从而真正的并发执行多任务多线程多线程-multithread/概念概念(0)如果一个程序被称为是多线程的如果一个程序被称为是多线程的,则该程序包括两条以上则该程序包括两条以上并发的独立执行部分并发的独立执行部分,每一个这样的并发执行部分被称为每一个这样的并发执行部分被称为一个线程一个线程(thread)与基于进程与基于进程(process-based)的执行环境相比较的执行环境相比较,基于线基于线程程(thread-based)的执行环境是多任务处理的一种特殊的执行环境是多任务处理的一种特殊形式形式多进程多进程环境中每一个进程既包括其所要执行的指令环境中每一个进程既包括其所要执行的指令, 也包也包括了执行指令所需的任何系统资源括了执行指令所需的任何系统资源, 如如CPUCPU、内存空间、内存空间、I/OI/O端口等端口等,不同进程所占用的系统资源相对独立不同进程所占用的系统资源相对独立多线程环境中每一个线程都隶属于某一进程多线程环境中每一个线程都隶属于某一进程,由进程触发由进程触发执行执行,在系统资源的使用上在系统资源的使用上,属于同一进程的所有线程共享属于同一进程的所有线程共享该进程的系统资源该进程的系统资源 多线程多线程-multithread/概念概念(1)在一个多线程运行环境中运行的线程可以有多种状态在一个多线程运行环境中运行的线程可以有多种状态,也就是说一也就是说一个线程在不同时刻会处于下列状态之一个线程在不同时刻会处于下列状态之一:正在运行正在运行(running);在等待运行在等待运行(runnable);被唤醒被唤醒(notify);被被阻塞阻塞(blocked);被终止被终止(terminate);多线程多线程-multithread/概念概念(2)阻塞阻塞终止终止新生成新生成一个线程一个线程等待等待运行运行start()yield()notify()wait(),sleep(),join()stop()Java中的多线程是建立在中的多线程是建立在Thread类类,Runnable接口的接口的基础上的基础上的通常有两种办法让我们来创建一个新的线程通常有两种办法让我们来创建一个新的线程w创建一个创建一个Thread类类,或者一个或者一个Thread子类的对象子类的对象w创建一个实现创建一个实现Runnable接口的类的对象接口的类的对象每一个线程都有自己的优先级每一个线程都有自己的优先级,如果不显式地说明如果不显式地说明,则被创则被创建的线程具有与创建它的线程相同的优先级建的线程具有与创建它的线程相同的优先级多线程多线程-multithread/创建线程创建线程(1)创建继承自创建继承自Thread类的子类,定义子类的属性、类的子类,定义子类的属性、方法,并覆盖父类的方法,并覆盖父类的run方法方法(把线程启动后将要执行的把线程启动后将要执行的代码方法在该方法中代码方法在该方法中)。(2)创建线程对象,调用创建线程对象,调用start方法启动线程。方法启动线程。=Thread(String name):参数指定线程名字:参数指定线程名字方法方法getName()返回线程名字返回线程名字方法方法setName(String name)更改线程名字更改线程名字=创建线程的方法一创建线程的方法一继承继承Thread类类 静态方法静态方法sleep(long millis):线程休眠,该方法可能:线程休眠,该方法可能抛出中断异常抛出中断异常InterruptedException静态方法静态方法activeCount():返回当前活动线程的数目:返回当前活动线程的数目静态方法静态方法currentThread():返回当前正在执行的线:返回当前正在执行的线程对象的引用程对象的引用=方法方法setPriority(int newPriority):设置优先级:设置优先级方法方法interrupt():中断线程:中断线程方法方法isAlive():测试线程是否处于活动状态。:测试线程是否处于活动状态。创建线程的方法一创建线程的方法一继承继承Thread类类 将一个类定义为将一个类定义为Thread的子类的子类, ,那么这个类就可以用那么这个类就可以用来创建线程。来创建线程。这个类中要重载这个类中要重载Thread的的public void run()方法方法: :这个方法称为线程体这个方法称为线程体,它是整个线程的核心它是整个线程的核心,线程所要线程所要完成任务的代码都定义在其中完成任务的代码都定义在其中,实际上不同功能的线程实际上不同功能的线程之间的区别就在于它们线程体的不同之间的区别就在于它们线程体的不同对于一个计算比给定值大的素数的线程对于一个计算比给定值大的素数的线程,也许会是如下也许会是如下的样子的样子:创建线程的方法一创建线程的方法一继承继承Thread类类class PrimeThread extends Thread long minPrime; PrimeThread(long minPrime) this.minPrime = minPrime; public void run() /compute primes larger than minPrime . . . public PrimeThreadDemo public static void main(String s) PrimeThread pt = new PrimeThread(100); pt.start(); 创建线程的方法一创建线程的方法一继承继承Thread类类public class MultiThreadDemo public static void main(String s) new MultiT().start(); try for(int i = 0; i 10; i+) System.out.println( Thread.currentThread().getName() + + i); Thread.sleep(1000); catch(InterruptedException ie) System.out.println(主线程被中断主线程被中断); System.out.println(主线程结束主线程结束); 转下页转下页多线程多线程/例例(1)-使用使用Thread接上页接上页class MultiT extends Thread MultiT() super(Second Thread); System.out.println(创建第二个线程:创建第二个线程:MutilT + this);public void run() try for(int i = 0; i 10; i+) System.out.println(getName() + + i); sleep(500); catch(InterruptedException ie) System.out.println(第二个线程被中断第二个线程被中断); System.out.println(第二个线程结束第二个线程结束); 多线程多线程/例例(1*)-使用使用Thread多线程多线程- multithread/Thread类介绍类介绍(1)Thread的构造方法的构造方法Thread();Thread(Runnable target);Thread(Runnable target, String name);Thread(String name);Thread(ThreadGroup group, Runnable target);Thread(ThreadGroup group, Runnable target, String name);Thread(ThreadGroup group, Runnable target, String name, long stackSize);Thread(ThreadGroup group, String name);name:线程的名称线程的名称,target:要运行的线程对象要运行的线程对象,group:线程组的名线程组的名称称,stackSize:堆栈的大小堆栈的大小多线程多线程- multithread/Thread类介绍类介绍(2)Thread的主要方法的主要方法(共共34个个,其中其中5个不再使用了个不再使用了)static Thread currentThread();int getPriority();String getName();ThreadGroup getThreadGroup();void interrupt();boolean isInterrupted();void join(); join(long millis); join(long millis, int nanos);static void yield();void setName(String name);void setPriority(int priority);void start();boolean isAlive(); static void sleep(long millis, int nanos);void run();static void sleep(long millis);countStackFrames(); suspend(); resume(); stop(); stop(Throwable obj); Java中提供了中提供了java.lang.Runnable接口接口实现实现Runnable接口接口如何用实现了如何用实现了Runnable接口的类对象创建一接口的类对象创建一个线程?个线程?Thread(Runnable target );Thread(Runnable target, String name);第一个构造方法创建的新线程名字自动生成,第一个构造方法创建的新线程名字自动生成,如如Thread-1,Thread-2等。等。第二个构造方法中的参数第二个构造方法中的参数name为线程的名字。为线程的名字。创建线程的方法二实现Runnable接口 创建线程的方法二实现Runnable接口Runnable接口只有一个方法接口只有一个方法run();一个实现一个实现Runnable接口的类接口的类,是为了去创建一个新的线程是为了去创建一个新的线程由于不能多重继承的原因由于不能多重继承的原因,以实现以实现Runnable接口的方式生成新线接口的方式生成新线程比扩展程比扩展Thread类的方法更一般化类的方法更一般化特别是在除了特别是在除了run()方法以外方法以外,并不打算重写并不打算重写Thread类的其它方法类的其它方法的情况下的情况下,以实现以实现Runnable接口的方式生成新线程就显得更加合接口的方式生成新线程就显得更加合理了理了Runnable是是Java中用以实现线程的接口,从根本上中用以实现线程的接口,从根本上讲,任何实现线程功能的类都必须实现该接口。讲,任何实现线程功能的类都必须实现该接口。Runnable接口中只定义了一个方法就是接口中只定义了一个方法就是run()方法,方法,也就是线程体也就是线程体Thread第二种构造方法中包含有一个第二种构造方法中包含有一个Runnable实例实例的参数,这就是说,必须定义一个实现的参数,这就是说,必须定义一个实现Runnable接接口的类并产生一个该类的实例,对该实例的引用就是口的类并产生一个该类的实例,对该实例的引用就是适合于这个构造方法的参数适合于这个构造方法的参数前面计算素数的线程也可以写成如下形式前面计算素数的线程也可以写成如下形式: :创建线程的方法二实现Runnable接口class PrimeRun implements Runnable long minPrime; PrimeRun (long minPrime) this.minPrime = minPrime; public void run() /compute primes larger than minPrime . . . public PrimeRunDemo public static void main(String s) PrimeRun pr = new PrimeRun(100); new Thread(pr).start(); 创建线程的方法二实现Runnable接口public class RunnableDemo public static void main(String s) new MultiR(); try for(int i = 0; i 10; i+) System.out.println( Thread.currentThread().getName() + + i); Thread.sleep(1000); catch(InterruptedException ie) System.out.println(主线程被中断主线程被中断); System.out.println(主线程结束主线程结束); 转下页转下页多线程多线程/例例(2)-使用使用Runnable接上页接上页class MultiR implements Runnable MultiR() t = new Thread(this, Second Thread Runnable); System.out.println(创建第二个线程:创建第二个线程:MutilR + this); t.start();public void run() try for(int i = 0; i java UnsynchPrint* Hello* 非同步多线程非同步多线程* program * * *E:zp北大方正北大方正CourseJavasrcMultiThreadsynchjava UnsynchPrint* Hello* 非同步多线程非同步多线程 * program * *多线程多线程/同步同步(synchronized)显然显然,在前面的例子里是因为允许多个线程在前面的例子里是因为允许多个线程“同时同时”使用使用write这这个方法个方法,才造成了输出不规矩的结果才造成了输出不规矩的结果如果我们采取措施如果我们采取措施,在一个线程使用在一个线程使用write方法时方法时,即使是它在即使是它在sleep也不让其它线程使用也不让其它线程使用,也就是说这个线程要将这个对象也就是说这个线程要将这个对象“锁锁定定”(lock)“锁定锁定”一个对象是使用关键字一个对象是使用关键字synchronized来实现的来实现的synchronized关键字可以使用在关键字可以使用在1.一个成员方法上一个成员方法上2.一个静态方法上一个静态方法上3.一个语句块上一个语句块上多线程多线程/同步同步(synchronized)关键字关键字synchronized的使用方法的使用方法(例例)public synchronized void write();public static synchronized int getValue();synchronized (obj) 一个对象的一个对象的synchronized方法只能被锁定它的线程调用方法只能被锁定它的线程调用,也就是也就是说说,一旦一个对象被某个线程锁定了一旦一个对象被某个线程锁定了,它的它的所有所有synchronized方法方法就都不允许别的线程调用了就都不允许别的线程调用了没有被没有被synchronized指定的方法指定的方法,别的线程还是可以调用的别的线程还是可以调用的多线程多线程/同步同步(synchronized)注意注意:由由synchronized关键字关键字锁定的是一个对象锁定的是一个对象,而不是一个方法而不是一个方法一个线程调用某个对象的一个线程调用某个对象的synchronized方法方法,就锁定了这个对象就锁定了这个对象直到它从这个方法中退出直到它从这个方法中退出,才释放了这个锁定才释放了这个锁定一个线程调用一个类的一个线程调用一个类的static synchronized方法方法,就锁定了这个就锁定了这个类对象类对象,也就控制了所有也就控制了所有static synchronized方法方法一个线程进入由一个线程进入由synchronized (obj) 修饰的程序块修饰的程序块,就锁定就锁定了由了由obj指定的那个对象指定的那个对象一个对象在某一时刻只能被一个线程锁定一个对象在某一时刻只能被一个线程锁定一个类可以有多个对象一个类可以有多个对象,不同的对象可以被不同的线程锁定不同的对象可以被不同的线程锁定一个线程可以锁定多个对象一个线程可以锁定多个对象多线程多线程/同步同步(synchronized)再来看一个例子再来看一个例子使用同步机制的输出使用同步机制的输出/* 使用同步使用同步-synchronized的多线程的多线程 */class PrintString1synchronized void write(String s) System.out.print(* + s); try Thread.sleep(1); catch(InterruptedException e) System.out.println( *);提示提示:在后台运行在后台运行synchBankTest程序程序/加了加了synchronized多线程多线程/同步同步(synchronized)接上页接上页class Printer1 implements RunnablePrinter1(PrintString1 ps, String msg) this.msg = msg; this.ps = ps; new Thread(this).start();public void run() ps.write(msg);private PrintString1 ps;private String msg;多线程多线程/同步同步(synchronized)接上页接上页public class SynchPrint public static void main(String s) PrintString1 ps = new PrintString1(); try new Thread(new Printer1(ps , Hello).join(); new Thread(new Printer1(ps , 同步多线程同步多线程).join(); new Thread(new Printer1(ps , program).join(); catch(InterruptedException e)多线程多线程/同步同步(synchronized)E:zp北大方正北大方正CourseJavasrcMultiThreadsynchjava SynchPrint* Hello * 同步多线程同步多线程 * program *E:zp北大方正北大方正CourseJavasrcMultiThreadsynchjava SynchPrint* Hello * 同步多线程同步多线程 * program *多线程多线程/同步同步(synchronized)wait,notify和和notifyAll都是与同步相关联的方法都是与同步相关联的方法,只有只有在在synchronized方法中才可以用方法中才可以用例如例如:public synchronized void banking() if(account.amountamount) wait(); 如果银行帐户里的金额比要转帐的金额少如果银行帐户里的金额比要转帐的金额少,就等待从别的就等待从别的地方存入足够的金额后再继续处理地方存入足够的金额后再继续处理这种情况也被称为这种情况也被称为“在同步方法中等待在同步方法中等待”多线程多线程/线程间通信线程间通信-wait,notify,notifyAll一个一个“在同步方法中等待在同步方法中等待” 的线程自己是没有办法恢复的线程自己是没有办法恢复运行的运行的,只能等另一个也进入了该对象的只能等另一个也进入了该对象的synchronized方法的线程调用方法的线程调用notify和和notifyAll后才有可能恢复运行后才有可能恢复运行例如例如:public synchronized void banking() if(account.amountjava WrongPCSet: 0Got: 0Set: 1Got: 1Set: 2Got: 2Set: 3Got: 3Set: 4Got: 4Set: 5Got: 5Got: 5Set: 6Got: 6多线程多线程/线程间通信线程间通信-例例1-5/* 正确的生产正确的生产/消费程序消费程序 */class Queueint n;boolean getFlag = false;synchronized int get() if(!getFlag) try wait(); catch(InterruptedException e) System.out.println(Got: + n); getFlag = false; notify(); return n;多线程多线程/线程间通信线程间通信-例例2-1接上页接上页synchronized void set(int n) if(getFlag) try wait(); catch(InterruptedException e) this.n = n; System.out.println(Set: + n); getFlag = true; notify();多线程多线程/线程间通信线程间通信-例例2-2接上页接上页class Producer implements RunnableProducer(Queue q) this.q = q; new Thread(this, Producer).start();public void run() int i=0; while(true) q.set(i+); try Thread.sleep(100); catch(InterruptedException e) private Queue q;多线程多线程/线程间通信线程间通信-例例2-3接上页接上页class Consumer implements RunnableConsumer(Queue q) this.q = q; new Thread(this, Consumer).start();public void run() while(true) q.get(); try Thread.sleep(100); catch(InterruptedException e) private Queue q;多线程多线程/线程间通信线程间通信-例例2-4接上页接上页public class CorrectPCpublic static void main(String s) Queue q = new Queue(); new Producer(q); new Consumer(q);多线程多线程/线程间通信线程间通信-例例2-5E:zp北大方正北大方正CourseJavasrcMultiThreadsynchjava CorrectPCSet: 0Got: 0Set: 1Got: 1Set: 2Got: 2Set: 3Got: 3Set: 4Got: 4Set: 5Got: 5Set: 6Got: 6Set: 7Got: 7Set: 8Got: 8多线程多线程/线程间通信线程间通信-例例2-6在使用同步方法时特别应该注意的问题是所谓在使用同步方法时特别应该注意的问题是所谓“死锁死锁”如果如果A线程在等线程在等B线程线程, B线程在等线程在等C线程线程, C线程线程在等在等A线程线程,就会形成就会形成“死锁死锁”另一种情况是在另一种情况是在wait,notify结构中结构中,如果所有的如果所有的线程都线程都wait了了,就不会有线程来就不会有线程来notify它们它们,因此因此要特别注意要特别注意多线程多线程/线程间通信线程间通信-wait,notify,notifyAll