【文档说明】Java语言程序设计第8章课件.ppt,共(45)页,154.094 KB,由小橙橙上传
转载请保留链接:https://www.ichengzhen.cn/view-45176.html
以下为本文档部分文字说明:
第8章线程郑莉JAVA语言程序设计2本讲内容线程基础–线程的概念–Thread类–Runnable接口–资源共享和线程同步–线程间的通信线程的生命周期线程的优先级程序举例3线程的概念进程:程序的一次执行
线程:一个进程中的多个控制流通过多线程,一个进程表面上看同时可以执行一个以上的任务——并发多数程序设计语言不支持并发,支持多线程要借助于操作系统“原语(primitives)”Java是第一个支持内置线程操作的主流编程语言线
程基础4Thread类简介线程类需要继承Thread类Thread的构造函数publicThread(StringthreadName)publicThread()在线程的run方法中编写线程的主要任务sleep方法使线程
休眠interrupt方法中断一个运行中的线程isAlive方法检查线程的状态setName方法设置线程名join方法等待线程结束,以执行当前线程线程基础5例8_1在新线程中计算整数的阶乘publicclassEx8_1{p
ublicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadthread=newFactorialTh
read(10);thread.start();System.out.println("newthreadstarted,mainthreadends");}//endmain}线程基础//classFactorialThreadcontrolsthreadexecuti
onclassFactorialThreadextendsThread{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){i
nti=num;intresult=1;while(i>0){result=result*i;i=i-1;}System.out.println("Thefactorialof"+num+"is"+result);System.out.println("newthreadends"
);}}运行结果如下:mainthreadstartsnewthreadstarted,mainthreadendsThefactorialof10is3628800newthreadends8例8_2创建3个新线程,每个线程睡眠任
意0-6秒钟,然后结束。publicclassEx8_2{publicstaticvoidmain(String[]args){//创建并命名每个线程TestThreadthread1=newTestThread("thread1");TestThreadthread2=newTestTh
read("thread2");TestThreadthread3=newTestThread("thread3");System.out.println("Startingthreads");thread
1.start();//启动线程1thread2.start();//启动线程2thread3.start();//启动线程3System.out.println("Threadsstarted,mai
nends\n");}}线程基础classTestThreadextendsThread{privateintsleepTime;publicTestThread(Stringname)//构造函数{super(name);//调用基类构造函数为线程命名//获
得随机休息毫秒数sleepTime=(int)(Math.random()*6000);}publicvoidrun()//线程启动并开始运行后要执行的方法{try{System.out.println(g
etName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);//线程休眠}catch(InterruptedExceptionexception){};//运行结束,给出提示信息Sys
tem.out.println(getName()+"finished");}}运行结果为:StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingt
osleepfor5565thread2finishedthread1finishedthread3finished11Runnable接口如果希望一个已经有基类的类支持多线程,则不能再继承Thread类了(Java不支持多继承)解决方法:–使类实现Runn
able接口,再与Thread类结合–使用一个内部类提供Runnable的实现代码,将内部类的对象与Thread结合相对于Thread类,它更适合于多个线程处理同一资源线程基础12例8_3使用Run
nable接口实现例8_1功能publicclassEx8_1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadt=newFactorialThread(1
0);newThread(t).start();System.out.println("newthreadstarted,mainthreadends");}//endmain}线程基础//classFactorialThrea
dcontrolsthreadexecutionclassFactorialThreadimplementsRunnable{privateintnum;publicFactorialThread(intnum){t
his.num=num;}publicvoidrun(){inti=num;intresult=1;while(i>0){result=result*i;i=i-1;}System.out.println("Thefactor
ialof"+num+"is"+result);System.out.println("newthreadends");}}14例8_4使用Runnable接口实现例8_2功能publicclassEx8_4{publicstaticvoidmain(String[]args){//创
建3个实现Runnable接口类的对象TestThreadthread1=newTestThread();TestThreadthread2=newTestThread();TestThreadthread3=newTestThread();System.out.println("Star
tingthreads");//分别以三个对象为参数创建三个新线程,//第二个参数为新线程命名并启动之newThread(thread1,"Thread1").start();newThread(thread2,"Thread2").start();newThread(thread3,"Threa
d3").start();System.out.println("Threadsstarted,mainends\n");}}线程基础classTestThreadimplementsRunnable{privateintsleepTime;pub
licTestThread()//构造方法{//获得随机休息毫秒数sleepTime=(int)(Math.random()*6000);}publicvoidrun()//线程启动并开始运行后要执行的方法{try{Sy
stem.out.println(Thread.currentThread().getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);//线程休眠}
catch(InterruptedExceptionexception){};//运行结束,给出提示信息System.out.println(Thread.currentThread().getName()+"finished");}}运行结果如下:S
tartingthreadsThread1goingtosleepfor1487Thread2goingtosleepfor1133Threadsstarted,mainendsThread3goingtosleepfor2328Thread2finishedT
hread1finishedThread3finished17资源共享和线程同步独立的同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作。–例如生产/消费问题:生产线程产生数据流,然后这些数据流再被消费线程消费。具体来说,假设一个Java
应用程序,其中有一个线程负责往文件写数据,另一个线程从同一个文件中往出都数据,因为涉及到同一个资源,这里是同一个文件,这两个线程必须保证某种方式的同步。当多个线程的执行代码来自同一个类的实例(若干个)时,即称它们
共享相同的代码,当共享访问相同的对象时,即它们共享相同的数据。–使用Runnable接口可以轻松实现多个线程共享相同数据,只要用同一个实现了Runnable接口的实例作为参数创建多个线程就可以了。线程基础18例8_5修改例8_4//只用一个Runnabl
e类型的对象为参数创建3个新线程。publicclassEx8_5{publicstaticvoidmain(String[]args){//只创建1个实现Runnable接口类的对象TestThreadt
hread=newTestThread();System.out.println("Startingthreads");//只用一个Runnable类型对象为参数创建三个新线程,//命名并启动之newThread(thread,"Thread1").sta
rt();newThread(thread,"Thread2").start();newThread(thread,"Thread3").start();System.out.println("Threads
started,mainends\n");}}线程基础classTestThreadimplementsRunnable{privateintsleepTime;publicTestThread()//构造函数{//获得随机
休息毫秒数sleepTime=(int)(Math.random()*6000);}publicvoidrun()//线程启动并开始运行后要执行的方法{try{System.out.println(Thread.
currentThread().getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);//线程休眠}catch(InterruptedExceptionexception){};//运行结束,给出提示信息Sys
tem.out.println(Thread.currentThread().getName()+"finished");}}StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor
966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished21例8_6用三个线程模拟三个售票口,总共出售200张票。pu
blicclassEx8_6{publicstaticvoidmain(String[]args){//新建一个售票类的对象SellTicketst=newSellTickets();//用此对象作为参数创建3个新线程并启动newThread(t).start();newT
hread(t).start();newThread(t).start();}}线程基础classSellTicketsimplementsRunnable{//将共享的资源作为私有变量privateinttickets=200;publicvoidrun(){while(ti
ckets>0)//直到没有票可售为止{System.out.println(Thread.currentThread().getName()+"issellingticket"+tickets--);}}}23例8_7用两个
线程模拟存票、卖票过程publicclassEx8_7{publicstaticvoidmain(String[]args){//新建一个票类对象,总票数作为参数Ticketst=newTickets(10);//以票类对象为参
数创建存票线程对象,并启动newProducer(t).start();//以同一个票类对象为参数创建售票线程,并启动newConsumer(t).start();}}线程基础classTickets//票类{intnumber=0;//票号intsize;//总票数//表示
目前是否有票可售booleanavailable=false;//构造方法,传入总票数参数publicTickets(intsize){this.size=size;}}classProducerextendsThread//存票线程{Ticketst
=null;publicProducer(Ticketst)//构造函数以一个票类对象为参数{this.t=t;}publicvoidrun(){//限制循环条件为存票序号小于总票数while((t.number)<t.size){
System.out.println("Producerputsticket"+(++t.number));t.available=true;//可以卖票}}}classConsumerextendsThread//售票线程{Ticketst=null;int
i=0;publicConsumer(Ticketst)//构造函数以一个票类对象为参数{this.t=t;}publicvoidrun(){while(i<t.size)//循环条件为售票序号小于总票数{if(t.available==true&&i<=t.number)//有票且小于目
前票序号System.out.println("Consumerbuysticket"+(++i));if(i==t.number)//如果票已售到当前序号,则不可售t.available=false;}}}运行结果Produ
cerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerput
sticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuyst
icket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10死机(进入死循环)28错误原因假如售票线程运行到t.available=false之前
,CPU切换到执行存票线程,存票线程将available置为true,但再次切换到售票线程后,售票线程执行t.available=false,则由于售票号目前还是8,小于总票数,且存票线程已经结束不再能将t.available置为true,则售
票线程陷入了死循环。线程基础29线程同步(Synchronization)Java使用的同步机制是监视器,支持线程的互斥与同步–互斥:许多线程在同一个共享数据上操作而互不干扰,同一时刻只能有一个线程访问该共享数据
。因此有些方法或程序段在同一时刻只能被一个线程执行,称之为监视区。–协作:多个线程可以有条件地同时操作共享数据执行监视区代码的线程在条件满足的情况下可以允许其它线程进入监视区。线程基础30线程间的通信线程同步关
键字——synchronized–用于指定需要同步的代码段或方法,也就是监视区。wait()–当前状态不适合本线程执行时,进入等待状态notify()–随机唤醒一个等待的线程,本线程继续执行。notifyAll()–唤醒所有等待的线程,本线程继续执行。线程基础31线程间的
通信线程被唤醒以后,还要等发出唤醒消息者释放监视器,这期间关键数据仍可能被改变。被唤醒线程的线程可能有多个。(使用notifyAll时)。被唤醒的线程开始执行时,一定要判断当前状态是否适合自己运行。线程基础32例8_8实现例8_7功能。将同步方法放在共享的资源类T
ickets中。publicclassEx8_8{publicstaticvoidmain(String[]args){Ticketst=newTickets(10);newProducer(t).start();n
ewConsumer(t).start();}}线程基础classTickets{intsize;//票总数intnumber=0;//存票序号inti=0;//售票序号booleanavailable=fa
lse;//是否有待售的票publicTickets(intsize){this.size=size;}publicsynchronizedvoidput()//同步代码块,实现存票的功能{System.out.prin
tln("Producerputsticket"+(++number));available=true;}publicsynchronizedvoidsell()//同步代码块,实现售票的功能{if(availab
le==true&&i<=number)System.out.println("Consumerbuysticket"+(++i));if(i==number)available=false;}}classConsumerextendsThread{Ti
cketst=null;//构造方法,使两线程共享票类对象publicConsumer(Ticketst){this.t=t;}publicvoidrun()//如果售票数小于限定总量,则不断售票{while(t.i<t.size)t.sell();}}35例8_9每存入一张票,就售一张票,售出后
,再存入publicclassEx8_8{publicstaticvoidmain(String[]args){Ticketst=newTickets(10);newProducer(t).start();
newConsumer(t).start();}}线程基础classTickets{intsize;intnumber=0;inti=0;booleanavailable=false;publicTi
ckets(intsize){this.size=size;}publicsynchronizedvoidput(){if(available)//如果还有存票待售,则存票线程等待try{wait();}catch(Exceptione){}System.out.println("Produce
rputsticket"+(++number));available=true;notify();...//存票后唤醒售票线程开始售票}publicsynchronizedvoidsell(){if(!available)//如果没
有存票,则售票线程等待try{wait();}catch(Exceptione){}if(i<=number)System.out.println("Consumerbuysticket"+(++i)
);if(i==number){try{Thread.sleep(1);}catch(Exceptione){}available=false;}notify();//售票后唤醒存票线程开始存票}}classProducerextendsTh
read{Ticketst=null;publicProducer(Ticketst){this.t=t;}publicvoidrun(){while(t.number<t.size)t.put();}}classConsumerextendsThread{Ticketst=null;pu
blicConsumer(Ticketst){this.t=t;}publicvoidrun(){while(t.i<t.size)t.sell();}}运行结果如下:Producerputsticket1Consumerbu
ysticket1Producerputsticket2Consumerbuysticket2Producerputsticket3Consumerbuysticket3Producerputsticket4C
onsumerbuysticket4Producerputsticket5Consumerbuysticket5Producerputsticket6Consumerbuysticket6Producerputsticket7Consumerbuysticket7Pr
oducerputsticket8Consumerbuysticket8Producerputsticket9Consumerbuysticket9Producerputsticket10Consumerbuysticket1041守护(Daemon)线程为了辅助其它线程而运行的
线程–不妨碍程序终止–“垃圾回收”便是一个守护线程用setDaemon方法将线程设置为守护线程线程基础42例8_10创建一个无限循环的守护线程publicclassEx8_10{publicstaticvoidmain(String[]args){ThreadTestt=newT
hreadTest();t.setDaemon(true);t.start();}}classThreadTestextendsThread{publicvoidrun(){while(true){}}}43线程的状态诞生状态–线程刚刚被创建就绪状态–线程的start方法已被执行–
线程已准备好运行运行状态–处理机分配给了线程,线程正在运行阻塞状态(Blocked)–在线程发出输入/输出请求且必须等待其返回,或遇到用synchronized标记的方法而未获得其监视器暂时不能进入执行时休眠状态
(Sleeping)–执行sleep方法而进入休眠死亡状态–线程已完成或退出线程的生命周期新线程ReadyStartRunningyield获得CPU资源WaitingSleepingBlocked运行直到结束notifynotifyAll等待时间到interrupt休眠时间
到interruptrun方法运行结束线程变为dead状态I/O操作进入synchronized语句未获得监视器Interrupt获得监视器I/O操作结束45线程优先级与调度Java线程优先级(priority)–取值范围1-10时间片(Timeslicing
)–每个线程被赋予一定的处理机时间–总是调度最高优先级的线程运行