您好,欢迎访问三七文档
第二章多线程一、线程概述单线程和程序只有一个顺序执行流,多线程的程序则可以包含多个顺序执行流,多个顺序流之间互不干扰。几乎所有的操作系统都支持多任务,每个任务就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。2.1.1线程和进程所有运行中的任务通常对应一条进程(Process)。当一个程序进入内存运行,则变成一个进程。进程是处于运行过程中的程序,并具有一定的独立功能。线程是进程的执行单元(组成部分),一个进程可以拥有多个线程,一个线程必须有一个父进程。线程是独立运行的,它并不知道进程中是否还有其他线程存在。线程的执行是抢占式的,也就是说,当前运行的线程在任何时候都有可能被挂起,以便另一个线程可以运行。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。简而言之:一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程。二、线程的创建和启动Java使用Thread类代表线程,所有的线程对象都必需是Thread类或其子类的实例。每条线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码)。Java使用run方法来封装一段程序流。2.2.1继承Thread类创建线程类1.定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务。因为,run方法也叫线程执行体。2.创建Thread子类的实例,即创建了线程对象。3.用线程对象的start方法来启动该线程。线程常用方法:start():启动线程getName():获取当前线程的线程名Thread.getCurrentThread():获取当前线程练习:编写一个线程类(FirstThread),定义局部变量i;线程执行体执行的操作为当i小于100时输出当前线程的名字以及I的值。编写main方法测试。循环100次,每次循环中输出当前线程的名字及I的值,并在第20次时启动两个FirstThread的实例(启动两个线程)publicclassFirstThreadextendsThread{privateinti;publicvoidrun(){for(;i100;i++){//当一个线程类继承了Thread类之后,可以通过getName()方法获取该线程的名称。System.out.println(this.getName()++i);}}publicstaticvoidmain(String[]args){for(inti=0;i100;i++){//调用Thread.currentThread()方法可以获取当前线程System.out.println(Thread.currentThread().getName()++i);if(i==20){//创建并启动第一个线程newFirstThread().start();//创建并启动第二个线程newFirstThread().start();}}}}PS:使用继承Thread类的方式创建线程,多条线程之间无法共享线程类的实例变量2.2.2实现Runnable接口创建线程类1.定义Runnable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是线程执行体。2.创建Runnable实现类的实例,并以此作为target来创建Thread对象。练习:改写上一个实例,创建类SecondThread实现Runnable接口,其他要求同上。(观察结果)publicclassSecondThreadimplementsRunnable{privateinti;publicvoidrun(){for(;i100;i++){System.out.println(Thread.currentThread().getName()++i);}}publicstaticvoidmain(String[]args){for(inti=0;i100;i++){//调用Thread.currentThread()方法可以获取当前线程System.out.println(Thread.currentThread().getName()++i);if(i==20){SecondThreadst=newSecondThread();//创建并启动第一个线程newThread(st,新线程1).start();//创建并启动第二个线程newThread(st,新线程2).start();}}}}三、线程的生命周期线程的生命周期中需要经过5个状态:新建、就绪、运行、阻塞和死亡。新建:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,和其他java对象一样,仅仅由java虚拟机分配内存,并初始化成员变量的值。就绪:线程对象调用start方法后,线程处于就绪状态,处于这个状态的线程并没有开始运行,只是表示线程可以运行了。至于线程何时运行,完全取决于JVM里线程调度器的调度。PS:永远不要去调用线程对象的run方法,如去调用线程对象的run方法就相当于把线程对象当成了普通对象去调用。运行:如果处于就绪状态的线程获得以CPU,开始执行run方法的线程执行体,则该线程处于运行状态。当线程数大于CPU的数量是,多条线程就会在CPU上轮换运行。阻塞:当一条线程开始运行后,并不可能一直处于运行状态,在运行过程中需要被中断。线程在调用sleep方法或等待通知(nodify)时会进入阻塞状态。死亡:当线程的run方法执行完毕、出现未捕获的异常或者直接调用stop方法等情况下线程便处于死亡状态。PS:当主线程结束时候,其他线程不受任何影响,并不会随之结束,一旦子线程启动便拥有与主线程相同的地位,不会受主线程的影响。测试某条线程是否已经死亡:isAlive():当线程处于新建、死亡状态时返回false,其他状态返回true。已死亡的线程不可再调用start()方法重新启用。四、控制线程2.4.1join线程join方法能使一个线程等待另一个线程。当在某个程序执行流中调用其他线程的join()方法时,调用的线程将被阻塞,直到被join的线程运行完成后再开始运行。join():等待被join的线程执行完成。join(longmills):等待被join的线程执行多少毫秒,过了时间后调用线程继续运行。练习:在main方法中创建启动一个线程并join调用。publicclassJoinThreadextendsThread{publicJoinThread(Stringname){super(name);}publicvoidrun(){for(inti=0;i100;i++){System.out.println(getName()++i);}}publicstaticvoidmain(String[]args){newJoinThread(新线程).start();for(inti=0;i100;i++){if(i==20){JoinThreadt=newJoinThread(被Join的线程);t.start();try{t.join();}catch(InterruptedExceptione){e.printStackTrace();}}System.out.println(Thread.currentThread().getName()++i);}}}2.4.2线程睡眠sleep让当前的执行线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep方法:sleep(longmills):让当前线程暂停mills毫秒,并进入阻塞状态。练习:在主线程的循环中暂停1秒publicclassSleepThreadextendsThread{publicstaticvoidmain(String[]args){for(inti=0;i100;i++){try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(i);}}}2.4.3线程让步yieldyield()方法与sleep方法有点类似,也是一个静态方法。可以让当前的线程暂停但不会阻塞该线程,只是将线程转入就绪状态。让线程调用yield()方法暂停后,只有优先级与当前线程相同,或者优先级大于当前线程的优先级的线程才会获得执行的机会。线程优先级:MAX_PRIORITY:值是10。MIN_PRIORITY:值是1。NORM_PRIORITY:值是5。publicclassYieldThreadextendsThread{publicYieldThread(Stringname){super(name);}publicvoidrun(){for(inti=0;i100;i++){System.out.println(getName()++i);if(i==20){Thread.yield();}}}publicstaticvoidmain(String[]args){YieldThready1=newYieldThread(高级);YieldThready2=newYieldThread(低级);y1.setPriority(Thread.MAX_PRIORITY);y2.setPriority(Thread.MIN_PRIORITY);y1.start();y2.start();}}2.4.4线程安全例银行取钱:1.用户输入账户、密码,系统判断用户的账户、密码是否匹配。2.用户输入取款金额。3.系统判断账户余额是否大于取款金额。4.如果余额大于取款金额,取款成功;如果余额小于取款金额,则取款失败。模拟类:Account,DrawThreadpublicclassAccount{privateStringaccountNo;privatedoublebalance;publicAccount(){super();//TODOAuto-generatedconstructorstub}publicAccount(StringaccountNo,doublebalance){super();this.accountNo=accountNo;this.balance=balance;}publicStringgetAccountNo(){returnaccountNo;}publicvoidsetAccountNo(StringaccountNo){this.accountNo=accountNo;}publicdoublegetBalance(){returnbalance;}publicvoidsetBalance(doublebalance){this.balance=balance;}publicinthashCode(){returnthis.accountNo.hashCode();}publicbooleanequals(Objectobj){if(obj!=null&&obj.getClass()==Account.class){Accountt=(Account)obj;returnt.getAccountNo().equals(this.accountNo);}returnfalse;}}publicclassDrawThreadextendsThread{privateAccountaccount;privatedoubledrawAmount;publicDrawThread(Stringname,Accountaccount,doubledrawAmount){super(name);this.account=account;this.drawAmount=drawAmount;}publicvoidrun(){if(account.getBalance()=d
本文标题:第二章-多线程
链接地址:https://www.777doc.com/doc-2185756 .html