java就业培训晰版第五章多线程_第1页
java就业培训晰版第五章多线程_第2页
java就业培训晰版第五章多线程_第3页
java就业培训晰版第五章多线程_第4页
java就业培训晰版第五章多线程_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

5线时执行多个应用程序,如我们接触最多的Windows、Linux、Unix。实际情况是,操作系统负责对CPU这就是两个不同的进程。我有一个朋友对我说,WindowsC盘向D又可从E盘向F盘拷贝文件,拷贝效率大为提高。大家对此话有何感想?我一听,就知道他充其量只能算是业余计算机者了,因为CPU只有一个,每个进程都有独立的代码和数据空间(进程上下C盘向DEF了解线程概创建多线程有两种方法:继承Thread类Runnable接口,在下面的小节里,我们分别进Thread类创建线Java的线程是通过java.lang.Thread类来控制的,一个Thread类的对象代表一个线程,而且只能代表一个线程,通过Thread类和它定义的对象,我们可以获得当前线程对象、获取某一线程的名称,可以实现控制线程暂停一段时间等功能,关于Thread类的具体应用与讲解,在文中JDKThreadThreadpublicclass{publicstaticvoid{new{System.out.println("mainthreadis}}}{publicvoid{{{System.out.println(Thread.currentThread().getName()+"isrunning");}}屏幕上不停地打印出mainisrunning,而不是mainthreadisrunning,这说码块1处的程序没有运行,因为代码块2先于代码块1运行,且代码块2为无限循环,代码块1没有机会运行。同时,我们也能够看到当前线程的名称为main。:publicclass{publicstaticvoidmain(String{newTestThread().start();{System.out.println("mainthreadis}}classTestThreadextends{publicvoid{{System.out.println(Thread.currentThread().getName()+"isrunning");}}}上面的代码让TestThread类继承了Thread类,也就是TestThread类具有了Thread类的全部特点,程序没有直接调用TestThread类对象的run方法,而是调用了该类对象从Thread类继承来的start方法。运行一下,我们能够看到两个while循环处的代码同时交替运行:可见,在单线,main函数必须等到TestThread.run函数返回后才能继续往下执行。而在多线,main函数调用TestThread.start()方法启动了TestThread.run()函数后,main函数不TestThread.runTestThread.runTestThread.runCPU个while循环处的代码同时交替运行。getName()方法,取出当前线程的runrunThreadThreadThreadrunThreadThreadrun运行的是Thread子类(也就是我们编写的那个类)对象中的run方法。我们可以通过控制run方法中的循环条件来控制线程的终止。JDKThreadThreadrun()方法作为其运行代码,具体细节大家已面看到了,请大家思考一个问题:如果Thread类的子类(在上面的例子中即TestThread)newThread().start();这样的语句,编译和运行时有明显的错误或异常吗?运行结果是怎样的呢?由于我们的线程对象不是通过Thread子类创建的,而是通过Thread类直接创建的,新的线程将直接调用Thread类中的run()方法,所以答案与上面的一样。使用Thread()构造方法,适用于覆盖了run方法的Thread子类创建线程对象的情况,如我们使用Runnable接口创建多线JDKThread(Runnabletarget)JDKRunnablerunThread(Runnabletarget)方Runnable那个实现了Runnable接口的类对象中的run()方法作为其运行代码,而不再调用Thread类中的run方publicclass{publicstaticvoidmain(String{//newTestThreadt.start();//使线程进入Runnable状{System.out.println("mainthreadis}}}classTestThreadimplementsRunnable//extends{{{System.out.println(Thread.currentThread().getName()+"isrunning");}}}两种实现多线程方式的对比分既然直接继承Thread类Runnable接口都能实现多线程,那么这两种实现多线程的方式票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程来表示。程序:ThreadDemo4.javapublicclassThreadDemo4{publicstaticvoidmain(String[]{ThreadTestt=newThreadTest();}}classThreadTestextends{privateinttickets=100;publicvoidrun(){{System.out.println(Thread.currentThread().getName()+"issalingticket"+tickets--);}}}能启动一个线程,无论你调用多少遍start()方法,结果都只有一个线程。publicclass{publicstaticvoidmain(String[]{newThreadTest().start();newThreadTest().start();newThreadTest().start();newThreadTest().start();}}图ThreadTest创建了四个资源,每个ThreadTest对象中都有100,每个线程在独立地处理各自的资源。能创建一个资源对象(该对象中包含要发售的那100,但要创建多个线程去处理这同一个资publicclass{publicstaticvoidmain(String[]{ThreadTestt=newThreadTest();newThread(t).start();newThread(t).start();newThread(t).start();newThread(t).start();}}classThreadTestimplements{privateinttickets=100;publicvoidrun(){{+"issalingticket"+tickets--}}}已经继承了某一个类的子类放入多线,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable5.1.5线程与联合线主线程也就随之结束了,这样的情况下,我们的这个java个进程就会结束。前台线程是相对线程而言的,我们按前面的方式产生的线程都是前台线程,就变成了线程。我们下面来看看,进只有线程运行时,进程就会结束的情况。publicclass{publicstaticvoidmain(String[]{ThreadTestt=newThreadTest();Threadtt=newThread(t);}}classThreadTestimplements{publicvoid{{System.out.println(Thread.currentThread().getName()+"is}}}线程,整个进程在主线程结束时就随之终止运行了。这验证了进只有线程运行时,进程就publicclass{publicstaticvoidmain(String[]{ThreadTestt=newThreadTest();Threadpp=newThread(t);inti=0;{{{}

catch(Exception{}}}}}classThreadTestimplements{publicvoid{Stringstr=newString();inti=0;{ }}}在上面的程序中用到了Thread类的join方法,即pp.join();语句,它的作用是把pp所对应的线程合并到调用pp.join();语句的线。在main线的循环计数达到100之前,我们看到mainThread-15.7中打印出来的结果。joinjoin(longmillisjoin(longmillis,intnanos),它join 多线程在实际中的应如果一方从键盘上了数据并发送给了对方,程序运行到“对方回送的数据”并一如果我们程序要将数据库一个表中的所有记录到另外一个表中,我们利用循环将源表中的的,代码放在一个循环语句中,循环条件由一个boolean变量来控制。如:booleanbFlag=true;{}多线的另外一个比较典型的例子就是WWW服务器,我们都知道,WWW的服务器是可以同时为线程安全问

多线程的同在上面卖车票的程序代码中,我们极有可能碰到一种意外,这就是同一号被打印两次或多次,也可能出现打印出0甚至负数的票号。这个意外发生在下面这部分代码处:System.out.println(Thread.currentThread().getName()+"issalingticket"+tickets--假设tickets的值为1的时候,线程1刚执行完if(tickets>0)这行代码,正准备执行下面的代码,就在这时,操作系统将CPU切换到了线程2上执行,此时tickets的值仍为1,线程2执行完上面两行代码,tickets的值变为0后,CPU又切回到了线程1上执行,线程1不会再执行System.out.println(Thread.currentThread().getName()+"issalingticket"+tickets--);时间(这里是毫秒)后,CPU回到刚才暂停的线程上执行。修改完的ThreadTest代码如下:classThreadTestimplements{privateinttickets=100;publicvoidrun(){{{{}

catch(Exception{}System.out.println(Thread.currentThread().getName()+"issalingticket"+tickets--);}}}}在上面的程序代码中,我们故意造成线程执行完if(tickets>0)语句后,执行Thread.sleep(10CPU看JDK文档中关于Thread.sleep()方法的定义如下:publicstaticvoidsleep(longmillis)throws通过throws关键字该方法中有可能异常,所以,我们的程序在调用该方法时,必须使用try…catchjava语言强健性的一个方面。Thread-2issalingticket3Thread-3issalingticket2Thread-4issalingticket1Thread-1issalingticket0Thread-2issalingticket-Thread-3issalingticket-这种意外问题,就是我们有时听到专业谈到的“线程安全”问题,也许某天就有人问你,System.out.println(Thread.currentThread().getName()+"issalingticket"+tickets--);即当一个线程运行到if(tickets>0)后,CPU不去执行其他线的、可能影响当前线的下classThreadTestimplements{privateinttickets=100;Stringstr=newString("");publicvoidrun(){{{{{}catch(Exception{}"issalingticket"+tickets--);}}}}}在上面的代码中,这些需要具有原子性的代码,放入synchronized语句内,形成了同后,其他线程才能进入同步代码块内运行。synchronized语句的格式为:synchronized(object){代码段 //object可以是任意的一个对Thread-2issalingticket3Thread-3issalingticket2Thread-4issalingticket任意类型的对象都有一个标志位,该标志位具有0、1两种状态,其开始状态为1,当执行synchronized(object)语句后,object0synchronized语句中的代码块后又回到1状态。一个线行到synchronized(object)语句处时,先检查object线程将暂时阻塞,让出CPU资源,直到另外的线行完有关的同步代码块,将object对象的标志位恢复到1状态,这个阻塞就被取消,线程能够继续往下执行,并将object对象的标志位变为0状(子一样),代码互之间的同步,只要各synchronized(objectobject当线行到synchronized的时候检查传入的实参对象,并得到该对象的锁旗标(就是我们synchronized(object)语句中的代码块的执行在试图进入已锁定的监视器时将被挂起,直到锁定了监视器的线行完synchronized(object)次去抢回来一样。另外当在同步块中遇到break语句或扔出异常时,线程也会释放该锁旗标。地对同步监视器进行检查,需要的开销。同步是以牺牲程序的性能为代价的,如果我们能够确放到run方法中定义:classThreadTestimplements{privateinttickets=100;publicvoidrun(){Stringstr=newString("");{{{{}catch(Exception{}"issalingticket"+tickets--);}}}}}编译运行后,发现结果又不正常了,问题出在什么地方呢?在这个程序中,run调用,相当于run方法被调用了四次,对每一次调用,程序都产生一个不同的str局部对象,同步函classThreadTestimplements{privateinttickets=100;publicvoidrun(){{}}publicsynchronizedvoid{{{}

catch(Exception{}"issalingticket"+tickets--);}}}synchronized代码块与函数间的同publicclass{publicstaticvoidmain(String[]{ThreadTestt=newt.str=new}}classThreadTestimplements{privateinttickets=100;Stringstr=newString("");publicvoidrun(){{{}}{{{{}

catch(Exception{}"issalingticket"+tickets--);}}}}publicsynchronizedvoid{{{}catch(Exception{}"issalingticket"+tickets--);}}}犯下众多错误,然后经过反复调试、观察、比较,最后总结、积累经验的过成长起来的。老手this0,就说明了这两个线程是不同步的,就达并非如此,程序在屏幕上最后打印出来的票号为1,屏幕上打印的结果的最后几行如下:Thread-2issalingticket3Thread-1issalingticket2Thread-2issalingticket的怀疑。修改后的sale方法如下:publicsynchronizedvoid{{{}

catch(Exception{}System.out.print("Test:");//新加的检测语句"issalingticket"+tickets--}}sale程上运行,并将str变量设置成了"method",等到第一个线程真正开始运行时,此刻检查到的str变量的值是"method",所以,它也去调用sale方法来运行。为了让第一个线程启动后马上就能得到CPU,我们可以在修改str变量值之前,让main线程暂停哪怕是仅仅的一毫秒就能达到我们的目的。对main方法进行修改后的代码如下:publicstaticvoidmain(String[]{ThreadTestt=newtry{Thread.sleep(1);}catch(Exceptione){}t.str=newString("method");}Test:Thread-2issalingticket3Thread-1issalingticket2Test:Thread-2issalingticket1Thread-1issalingticket0结果,认为上面的程序是能够程间同步的,并作为一个经验心得向人炫耀。可见,在计算机编thispublicvoidpush(char{}在实际项目中,多线程安全问题会有许多。多线程共享数据,要十分,线程同步的错死锁问出现死锁。例如,一个线程进入对象X的监视器,而另一个对象进入了对象Y的监视器,这时进X对象监视器的线程如果还试图进入Y对象的监视器就会被阻隔,接着进入Y对象监视器的线程如锁条件。foo和bar使用sleep来强制死锁条件出现。在现实程序中死锁是较难发现的:class{synchronizedvoidfoo(B{System.out.println(name+"enteredA.foo");{}catch(Exception{}System.out.println(name+"tryingtocall}synchronizedvoid{System.out.println("inside}}class{synchronizedvoidbar(A{System.out.println(name+"enteredB.bar");{}catch(Exception{}System.out.println(name+"tryingtocall}synchronizedvoid{System.out.println("inside}}classDeadlockimplements{Aa=newBb=newB();{newThread(this).start(); //getlockonainthisthread.System.out.println("backinmain}publicvoid{b.bar(a);//getlockonainotherthread.System.out.println("backinother}publicstaticvoidmain(String[]{new}}从运行结果可以看出,RacingThreadba我们通过这样的一个应用来讲解线程间的通信。有一个数据空间,划分为两部分,一部分用于人的,另一部分用于人的。我们的应用包含两个线程,一个线程向数据空间添充数据(生产者,另一个线程从数据空间中取出数据(消费者。这个程序有两种意外个过程可用图5.12表示:问题如何解此我们需要编写两个包含有run方法的类来完成这两个线程,一个是生产者类Producer,一个是消费者类Consumer。classProducerimplements{publicvoid{{}}}classConsumerimplements{publicvoid{{编写从数据空间中数据的代}}}class{Stringname; }ProducerConsumerrunQProducer和Consumer这两个类作如下修改,顺便也写出程序的主调用类 classProducerimplements{QpublicProducer(Q{}publicvoid{inti=0;{{=" ="男}{=" ="女}}}}class{ }classConsumerimplements{QpublicConsumer(Q{}publicvoid{{System.out.println(+"---->"+q.}}}public {publicstaticvoidmain(String[]{Qq=newnewThread(newProducer(q)).start();newThread(new}}在上面的代码中,Producer和Consumer都定义了一个类Q的成员变量,并通过各自的构造函PrdcrCnuer对象,这样,Producer和Consumer的就是同一个Q对象了。为了便于观察程序运行的效果,在Producer的run方法中,我们在每次循环替地存放两个人员数据内容:“陈CU在PourCnuer特殊情况(实际上就存在这样的可能性,只是我们不太容易捕捉到这种情况,我们在上面程序中的="q.="男{}catch(Exception{} ="男classProducerimplements{QpublicProducer(Q{}publicvoid{inti=0;{{{{}catch(Exception{} ="男}{=" ="女}}}}}class{ }classConsumerimplements{QpublicConsumer(Q{}publicvoid{{{System.out.println(+"---->"+ }}}}程序虽然解决了线程同步的问题,但这样的程序结构比较,显得条理不清,有点令人感到一种说不出来的别扭。我们为什么不在类Q中增加两个方法,一个对成员变量赋值,另一个取值,class{privateStringname="";privateString ="女";publicsynchronizedvoidput(String {{}catch(Exception{} }publicsynchronizedvoid{System.out.println(name+"---->" }}classProducerimplements{QpublicProducer(Q{}publicvoid{inti=0;{}}}classConsumerimplements{QpublicConsumer(Q{}publicvoid{{}}}对成员变量的都通过类中的具有public权限的方法来进行,这样定义的类才是面向对象从上面程序的执行结果上来看,Consumer线程对Producer线程放入的一次数据连续了多Object如果想让上面的程序符合我们的需求,须在类Q中定义一个新的成员变量bFull来表示据后,bFulltruebFulltrue,ConsumerProducer线程放入新的数据后,反之,只有bFull为false,Producer线程才能放入新的数据,否则就必须等待Consumer线程取走数据后。修改后的Q类的程序代码如下:class{privateStringname="";privateString booleanbFull=false;publicsynchronizedvoidput(String {{}catch(Exception{} }publicsynchronizedvoid{System.out.println(name+"---->" }}同组里的wait只能被同组的notify唤醒。请读者结合我们前面讲的内容的这。如图5.14所示,一个线程的产生是从我们调用了start方法开始进入Runnable状态,即可以它才会被唤醒或打断开始等待对象锁旗标,等到锁旗标后进入Runn

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论