1、第八章 异常处理与多线程精品资料第八章 异常处理与多线程8.1 异常就是Java程序在运行过程中出错, 异常终止的情况.可能导致异常发生的原因有许多,如数组下标越界,空指针访问,试图读取不存在的文件,网络连接中断等.j3_27.java - 数组下标越界违例(Exception)class Testpublic static void main(String args) String friends=lisa,bily,kessy; /创建字符串一维数组有3个元素值for(int i=0;i5;i+) /下标有2个越界异常发生System.out.println(friendsi); Syst
2、em.out.println(nthis is the end);Java中异常分两大类: (1)错误(Error) - JVM系统内部错误,资源耗尽,内存溢出的严重情况,程序员无能为力,只能让程序终止.(2)违例(Exception) - 编程错误或偶然外在因素导致的一般性错误.如对负数开方, 空指针访问,试图读取不存在的文件,网络连接中断.Exception可预先预防. Java异常类层次关系图(1)运行时异常(RuntimeException)是因程序员设计或实现方式不当造成的.如果事先检查数组元素下标不超出数组长度,异常就不会抛出.(2)非运行时异常(Non-RuntimeExcept
3、ion)-由编译器编译时发生的异常,而不是程序本身的错误,如IOException有时是环境造成的,如文件未找到或URL无效等.异常处理Java有两种方式:(1) 使用 trycatch.finally语句捕获并处理异常对象,其中finally语句可省略. 通常将可能发生异常的语句放在try语句中.(2) 不需处理它生成的异常,而是向上传递,由调用它的方法来处理,使用throws语句声明它可以抛出的异常,而不捕捉它们.(1) j3_28.java 使用 trycatch.finally语句捕获数组下标越界异常并处理异常对象(也称运行时异常RuntimeException)class Test
4、public static void main(String args) try /选定捕获异常范围String friends=lisa,bily,kessy;for(int i=0;i5;i+) /试图访问不存在的元素 System.out.println(friendsi); / catch语句需要一个形参,指明捕获异常类型对象, 异常发生时程序执行catch体内的语句. catch (Exception e) System.out.println(e); finally /无论异常是否发生finally体内的语句总是被执行 System.out.println( Its ok!); S
5、ystem.out.println( Out of Try!);(2)ExceptionTest3.java -非运行时异常(编译器编译时发生的异常-Java编译器从语法上要求对异常事件做出处理,否则不能通过编译)例如:ExceptionTest3.java - 不能通过编译,改正为ExceptionTest4.java就可编译。import java.io.*;public class ExceptionTest3 public static void main(String args) /创建字节输入流对象 FileInputStream in=new FileInputStream(my
6、file.txt); int b;b = in.read(); /读文件 while(b!= -1) System.out.print(char)b); b = in.read(); in.close(); ExceptionTest4.java -(文件不存-非运行时异常)- (使用 trycatch.finally语句捕获并处理异常对象)import java.io.*;public class ExceptionTest4 public static void main(String args) try / try. 语句块选定捕捉异常的范围,如执行的语句块有异常则抛出异常对象FileI
7、nputStream in=new FileInputStream(myfile.txt); /执行的语句块int b;b = in.read();while(b!= -1) System.out.print(char)b);b = in.read();in.close(); catch (IOException e) /异常对象被抛出将被catch (IOException e)语句捕获,异常对象作为参数传入,捕获异常对象有有多种,也就是可有多个catch语句,此时为IOException e, System.out.println(e); /输出异常信息finally /善后语句无论try
8、. 语句是否发生异常finally语句都会执行 System.out.println( Its ok!); (3) j3_29.java 使用throws语句声明抛出异常对象给调用方用的catch()方法捕获异常并处理异常import java.io.*;class Testpublic static void main(String args)Test t = new Test ();try t.readFile(); /调用readFile()方法(在调用方readFile()方法中的catch()方法中捕获到这个异常并处理) catch(IOException e) System.out
9、.println(e); System.out.println(退出!); /在readFile()方法中声明IOException异常对象, 异常发生时该方法并不进行异常处理,而是抛出异常对象给调用方readFile()方法处理,.public void readFile() throws IOException FileInputStream in=new FileInputStream(myfile.txt); /myfile.txt文件不存在int b;b = in.read();while(b!= -1) System.out.print(char)b);b = in.read();
10、in.close();(4)Testthrow1.java-手工抛出异常import java.io.*;public class Testthrow1 static void readArray(int a,int b) /静态读数组方法/ 如果ab手工抛出数组下标越界异常对象并调用捕捉异常方法输出异常提示 try if (ab) /抛出数组下标越界异常对象并在readArray方法中的catch捕获输出很可惜超过数组范围!信息 throw new ArrayIndexOutOfBoundsException (很可惜超过数组范围!); else int array=new inta; /创
11、建数组长度为a的整型数组 arrayb=11; /在下标号为4位置处赋值11给数组 System.out.println(数组赋值成功!); catch(ArrayIndexOutOfBoundsException e) System.out.println(readArray方法异常提示:+e.getMessage (); throw e; / throw e抛出错误给调用方及main方法的readArray(4,6)方法的catch捕获 public static void main(String args) try/调用readArray(6,4) 静态方法并传递参数a=6,b=4,执行
12、else后语句为数次组合法赋值 readArray(6,4); readArray(4,6); /数组元素为4即长度为4,下标为6数组下标越界异常 catch(ArrayIndexOutOfBoundsException e) System.out.println(main方法异常提示:+e.getMessage (); (5)j3_30.java-自定义抛出异常类class MyException extends Exception private int idnumber; /声明整型变量idnumber代表出错代码public MyException(String message, in
13、t id) /声明MyException()异常方法 super(message); /返回父类信息this.idnumber = id; / MyException对象出错id为3代表注册人数为负值,不合理 public int getId() / getId()方法,取得出错Idreturn idnumber; class Test/在regist(int num)方法中声明MyException异常,并抛出异常对象public void regist(int num) throws MyException if (num 0) /注册人数 0/ 抛出异常对象new MyException
14、(人数为负值,不合理,3); 参数传到MyException()方法中设置出错种类为3并取得错误代码3,同时将抛出异常对象给调用方的catch捕获输出异常原因即登记失败,出错种类3throw new MyException(人数为负值,不合理,3); System.out.println(登记人数+ num);public void manager() /管理注册方法try /设计错误及注册人数-100在调用regist(int num)方法时满足抛出异常对象,并执行catch语句regist(-100); catch (MyException e) System.out.print(登记失败
15、,出错种类:+e.getId()+n);System.out.print(本次登记操作结束);public static void main(String args) / main()入口Test t = new Test();t.manager();8.2 多线程 (java中引入多线程机制的目的在于实现多任务,以提高程序利用CPU的效率.主要通过多线程之间共享代码和数据来实现的.)多线程是根据多任务的原理,在一个程序内部实现多个任务(顺序控制流)的并发执行.其中的每个任务被称为线程(Thread), 线程是一个程序内部的顺序控制流.(1)进程 执行一个程序的过程(包括分配内存外设等资源等侯
16、处理器的执行程序中的指令程序执行完后系统回收所分配资源的过程),每个进程有自己的内存空间和资源进程之间不会共享系统资源,进程切换开销大。(2)线程 - 线程是比进程更小的运行单位,一个进程可划分成多个线程。线程由操作系统调度独立执行控制,和进程的区别是没有独立的存储空间,而是和所属进程的其它线程共享一个存储空间,即共享进程资源,所以线程之间数据通信交换比进程切换快。(3)每个Java程序都有一个默认主线程,这个主线程从main方法开始并负责执行main方法。在main方法的执行中再创建的线程称为其他线程,如果main方法的执行中没有创建其他线程,那么main方法执行完最后一条语句,及main方
17、法返回时,JVM就会结束Java应用程序,如果main方法中再创建了其他线程,那么JVM就会在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU,直到JVM执行完所有线程才会结束Java应用程序。因此通过多线程技术可提高CPU的使用效率和多程序的并发执行,完成多任务。(4)线程随着程序的运行而产生, 随着程序的结束而消亡.每个线程都存在一个从新建,运行到死亡的生命周期.在生命周期中,一个线程具有新建,就绪, 运行, 阻塞和终止5种状态。【例9.1】Example9_1.java public class Example9_1 public static void main(Strin
18、g args) Lefthand left; Righthand right; left=new Lefthand() ; /创建左线程 right=new Righthand();/创建右线程 left.start(); /左线程开始 right.start(); /右线程开始 for(int i=1;i=6;i+) System.out.println(我是主线程); class Lefthand extends Thread public void run() for(int i=1;i=9;i+) System.out.println(我是左手线程); class Righthand
19、extends Thread public void run() for(int i=1;i=9;i+) System.out.println(我是右手线程); 8.3 线程的基本概念每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的, 方法run( )称为线程体.8.4 线程实现的两种方法(1) 使用Thread类的子类方法创建线程步骤1. 定义一个类继承Thread类,重写Thread类中的run( )方法2. 创建该Thread子类的对象3. 调用该对象的start( )方法时, run( )方法将被自动调用,线程启动.【例8-1】j3_39.java-用Th
20、read类的子类方法创建线程class TestThread3 public static void main(String args)/(2)创建该Thread子类的对象,即是创建一个线程(或是创建了一个虚拟的CPU)Thread t = new Runner3(); t.start(); /(3)调用该对象的start( )方法时, run( )方法将被自动调用线程被启动 class Runner3 extends Thread /(1)定义一个子类Runner3继承Thread类public void run() /线程体(重写Thread类中的run( )方法)for(int i=0;
21、 i30; i+) /线程对象运行System.out.println(No. + i);结果: No.0 No.29此方法直接继承Thread类创建线程时, Thread类的子类Runner3无法从其他类继承, run( )方法的当前对象就是线程对象,可直接操纵. 这种方法简单. java中只允许单继承,限制了类的自由,浪费了类的资源,因此采用继承Thread类来创建线程不是最好的方法.(2) 实现Runnable接口创建多线程步骤运行线程的另一种方法是实现Runnable接口,然后生成运行这个类的线程即可. Runnable接口是定义在java.lang包中的一个接口,其中只提供了一个抽象
22、的run( ) 方法,即线程体.1. 定义一个类实现Runnable接口,重写其中的run( )方法.2. 创建Runnable接口实现类的对象.3. 创建该Thread类的对象(以先前Runnable类型对象为构造方法实参)4. 调用Thread对象的start( )方法时, run( )方法将被自动调用,启动线程.【例8-2】j3_38.java-用Runnable接口创建多线程class TestThread2 public static void main(String args) /2. 创建Runnable接口实现类的对象rRunner2 r = new Runner2();/ 3
23、. 创建该Thread类的对象(以先前Runnable类型对象r为实参), 共创建两个线程t1, t2Thread t1 = new Thread(r); Thread t2 = new Thread(r);t1.start(); / 4.调用Thread对象的start( )方法时, run( )方法自动运行,启动2个线程.t2.start();/ 1.定义Runner2类实现Runnable接口,重写其中的run( )方法.class Runner2 implements Runnable public void run() /重写run( )方法for(int i=0; i20; i+)
24、 / 用Thread类的静态方法Thread.currentThread()取得当前正在执行的线程对象,并通过它的getName()方法获得线程名.String s = Thread.currentThread().getName();System.out.println(s + : + i);在本例中创建了两个新线程t1, t2,它们共享Runner2()类中的run( )方法,同时也共享创建Runnable接口实现类的对象r, 两个线程t1, t2在运行中分别操纵对象r调用其重写run( )方法.其结果线程t1, t2作为独立的顺序控制流,并发地交替执行,如果线程t1因某种原因处于阻塞状态
25、,如等待用户键盘输入,CPU会立即转到线程t2执行,依此类推,而不必空置CPU.使用Runnable接口创建线程时,可以将CPU,代码和数据分开,形成清晰的模型. 线程体.run( ) 方法所在的类还可从其它类继承一些有用的属性和方法,保持程序风格一致性.【例9.1】Example9_3.java public class Example9_3 public static void main(String args ) Bank bank=new Bank(); /创建银行对象 /线程的目标对象设置被线程共享的moneybank.setMoney(300); /存300元钱到银行中 bank.
26、会计.start(); /线程对象会计开始记帐 bank.出纳.start(); /线程对象出纳开始支付 class Bank implements Runnable /声明银行类实现Runnable接口 private int money=0; Thread 会计,出纳; /声明线程对象会计,出纳 Bank() /构造函数 会计=new Thread(this); /创建线程对象会计 会计.setName(会计); /设置线程名会计 出纳=new Thread(this); /创建线程对象出纳(会计和出纳线程的目标对象相同) 出纳.setName(出纳); /设置线程名出纳 public v
27、oid setMoney(int mount) /存钱方法 money=mount; /存300元 public void run() /接口中的方法(线程体) while(true) money=money-50; / money=300-50=250 if(Thread.currentThread()=会计) System.out.println(我是+会计.getName()+现在有:+money+元); if(money=150) System.out.println(会计.getName()+进入死亡状态); return; /如果money少于150, 会计的run方法结束,会计线
28、程进入死亡方法 else if(Thread.currentThread()=出纳) System.out.println(我是+出纳.getName()+现在有:+money+元); if(money=0) return; /如果money少于0, 出纳的run方法结束,出纳线程进入死亡方法 try Thread.sleep(800); /让线程休眠800毫秒 catch(InterruptedException e) 【例8-3】TestThread1.java -class TestThread static AThread aThread;static BThread bThread;
29、 public static void main(String args) aThread=new AThread() ; /创建两个线程类对象AThreadBThread。 bThread=new BThread(); aThread.start(); /起动线程 bThread.start(); class AThread extends Thread /声明AThread类 public void run() for(int i=0;i=2;i+) System.out.println(I am A+i);trysleep(600); /休眠600毫秒 catch(Interrupted
30、Exception e) class BThread extends Thread /声明BThread类 public void run() for(int i=0;i=2;i+) System.out.println(I am B+i); trysleep(400); /休眠400毫秒 catch(InterruptedException e) 从结果看出aThread休眠时间长,故切换到执行BThread,run()方法中的局部变量值不会影响其它线程的run()方法中的局部变量值3.8.3 线程的状态线程随着程序的运行而产生, 随着程序的结束而消亡.每个线程都存在一个从新建,运行到死亡的
31、生命周期.在线程生命周期中,一个线程具有新建,就绪, 运行, 阻塞和终止5种状态, Thread类中的方法可以改变线程的状态.在整个生命周期中, 线程对象总是处于5种生存状态中某一种.如图7.4线程在5种生存状态中转换.(1) 新建线程(new thread)- 用new创建一个线程处于新建状态。它仅是一个空对象,并未得到系统资源。(2) 就绪状态(runnable )- 用start( )方法启动一个线程后,系统为该线程分配资源。此时该线程进入线程对列排队,即线程进入就绪状态,等待处理器执行。(3) 运行状态(running )- 系统调度一个可运行线程,使该线程占用处理器,执行该线程的ru
32、n( )方法,此时线程进入运行状态。(4) 阻塞状态 (Not runnable)- 由于某种原因线程不能运行,即使处理器空闲,该线程也不会执行。如输入输出等待,睡眠,和锁定等原因。只有阻塞原因被消除,该线程才进入运行状态,并再次进入线程对列排队等待处理器从上次中断处继续运行。(5) 终止状态(dead)- 线程执行完毕进入终止状态,也就是run()方法执行完,该线程自然撤消。终止有两个原因:自然撤消或被停止。isAlive()方法可返回线程运行状态(是否)。3.8.4 对线程状态的控制在对线中有若干方法改变线程状态。(1) 使线程进入睡眠状态的sleep( )方法 public static
33、 void sleep( long millis) throws InterruptedException millis -睡眠时间(毫秒)sleep( )方法使线程进入睡眠状态,即不可运行状态时抛出一个中断异常InterruptedException。【例8-4】J3_40.java使用sleep( )方法使线程状态转换class TestThread3public static void main(String args) Runner3 r = new Runner3(); /2.创建Runnable接口实现类的对象rThread t = new Thread(r); /3.创建线程t,
34、处于新建状态t.start(); /4.启动线程进入就绪状态class Runner3 implements Runnable /1.定义类Runner3实现Runnable接口 public void run() /执行该线程的run()方法进入运行状态for(int i=0; i200) x=0; if(y100) y=10; repaint(); /重写paint()方法绘制字符串 try Scrollwords .sleep(20); /Thread休眠20毫秒可看到字符串变动位置 catch(InterruptedException e) public void paint(Grap
35、hics g) g.drawString(Here is big club,welcome to all!,x,y); /在坐标处绘制字符串 public void stop() Scrollwords.yield(); /暂停线程程序的运行,但线程仍在可运行状态(末进入阻塞状态) Scrollwords =null; /设线程为空,则又运行程序 D:Java_UJava_Code08TestThread4.html(3) join( ) 方法join( ) 方法使本线程暂停执行,直到调用该方法的线程执行结束后再继续执行本线程。本线程要等到调用该方法的线程结束后再继续执行,本书称线程联合。【例
36、8-6】TestThread5.java使用join( )方法public class TestThread5 public static void main(String args)Athread a = new Athread(); /2.创建Athread类对象实例 Thread t = new Thread(a); /3.创建该对象实例的线程 t.start(); /4.启动线程进入就绪状态并调用run()方法进入该线程运行状态 try t.join();/暂停执行执行for循环代表的main主线程,执行完Athread线程后才执行main主线程 catch(InterruptedException e) for(int i=0;i3;i+) /执行for循环代表的main主线程 System.out.println(main主线程: + i); class Athread implements Runnable /1.定义Athread类实现Runnable接口public void run()