版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本模块通过3个任务主要介绍线程的概念、线程的创建、线程的生命周期及状态转换、线程的调度、多线程同步以及线程通信。模块介绍思维导图教学大纲能力目标◎会使用多种方式创建线程◎会使用线程同步优化程序◎会使用多线程通信完成要求功能知识目标◎了解多线程的概念◎掌握多线程创建的方式◎理解线程的生命周期及状态转换◎掌握线程的调度◎掌握多线程同步◎掌握多线程通信教学大纲学习重点◎多线程的创建◎线程的同步学习难点◎多线程通信任务8.1多窗口售票程序设计任务目标了解多线程的概念掌握多线程创建的方式任务8.1多窗口售票程序设计任务描述要求程序实现开启多个窗口售票,运行效果如下图所示:图8-1任务1运行效果图任务8.1多窗口售票程序设计知识准备8.1.1线程概述
前面所有的案例程序都是从main()方法入口开始执行到程序结束,整个过程只能顺序执行,如果程序在某个地方出现问题,则程序就会崩溃。这种程序因为是单线程的,比较脆弱和局限,如果用来实现售票程序,则相当于只能开启一个售票窗口进行售票。如果我们需要开启多个窗口售票,则需要使用多线程技术。
在程序设计中,多线程就是指一个应用程序中有多条并发执行的线程,它们交替执行,且彼此间可以通信。任务8.1多窗口售票程序设计图7-2I/O流的顶层类1.进程的概念
在操作系统中,每个独立执行的程序都可称之为一个进程。例如同时运行微信、QQ、音乐播放软件等。
在多任务操作系统中,我们可以一边听音乐,一边处理邮件,但实际上这些进程并不是在同一时刻运行的。因为同一时刻CPU只能运行一个进程,是操作系统为每一个进程分配一段有限的CPU使用时间片段,每个CPU时间片段执行一个进程,由于CPU速度很快,可以在极短的时间内在不同进程间切换,给我们的感觉就是多个程序同时运行。任务8.1多窗口售票程序设计图7-2I/O流的顶层类2.线程的概念
在多任务操作系统中,在一个进程中可以有多个执行单元同时运行,来完成程序任务,这些并行执行的单元就是线程。每个进程至少存在一个线程,但Java程序启动时,就会产生一个进程,进程中会默认创建一个线程,在这个线程中会运行main()方法中的代码。
如果希望程序中实现多段程序代码交替运行,则需要创建多个线程,即多线程程序。多线程程序在运行时,每个线程之间都是独立的,可以并发执行,可以互相通信。表面上看多线程是并发执行的,实际上它们和进程一样,也是轮流使用CPU时间片段执行的,只是因为CPU速度很快,且每个时间片段极短,给我们的感觉是多线程并行执行。任务8.1多窗口售票程序设计8.1.2线程的创建在Java语言中,可以使用三种方式来实现多线程:继承Thread类实现Runnable接口实现Callable接口任务8.1多窗口售票程序设计Thread类实现多线程说明:在java.lang包下有一个线程类Thread,可以通过继承Thread类来实现多线程。步骤:1)首先需要先创建一个自定义的子线程类,继承自Thread类,同时需要重写Thread类的run()方法。2)然后创建子线程类的实例对象;3)并调用类的start()方法启动子线程。案例:例8-1Example8_1.java任务8.1多窗口售票程序设计Thread类实现多线程案例:例8-1Example8_1.java//自定义一个子线程类,继承自Thread类
classMyThreadextendsThread{
//构造方法,初始化子线程名
publicMyThread(Stringname){
super(name);
}
//重写run()方法,方法内是子线程要执行的语句
publicvoidrun(){
for(inti=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":子线程在运行");
}
}
}
任务8.1多窗口售票程序设计2.Runnable接口实现多线程说明:Java只支持类的单继承,一旦某个类已经继承了其他父类,则无法再继承Thread类来实现多线程。这种情况下,就可以通过实现Runnable接口来实现多线程。步骤:1)创建Runnable接口的实现类,重写接口的run()方法;2)创建Runnable接口的实现类对象;3)创建线程实例;4)调用start()方法启动线程。案例:例8-2Example8_2.java任务8.1多窗口售票程序设计2.Runnable接口实现多线程案例:例8-2Example8_2.java//定义Runnable接口的实现类classMyRunnableimplementsRunnable{//重写接口的run()方法@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":子线程在运行");}}}任务8.1多窗口售票程序设计3.Callable接口实现多线程说明:通过实现Runnable接口来实现多线程时,由于run()方法没有返回值,因此无法从子线程获得返回结果。从JDK5开始,Java提供了Callable接口用来实现多线程,该实现可以从子线程获取返回值的需求。使用Callable接口实现多线程需要用到类FutureTask,构造线程对象的时候传递的是FutureTask类对象而不再是接口的实现类对象。步骤:1)创建Callable接口的实现类,重写接口的call()方法;2)创建Callable接口的实现类对象;3)使用类FutureTask先封装Callable接口实现类对象得到FutureTask类对象;4)使用Tread类创建线程实例,传递参数为FutureTask类对象;5)调用start()方法启动线程。任务8.1多窗口售票程序设计3.Callable接口实现多线程案例:例8-3Example8_3.java//定义Callable接口的实现类classMyCallableimplementsCallable{//重写重写接口的call()方法@OverridepublicObjectcall()throwsException{inti=0;for(;i<3;i++){System.out.println(Thread.currentThread().getName()+
":子线程call()方法在运行");}returni;}}任务实施根据任务分析可知:(1)售票厅有3个窗口可发售某日某次车的50张车票;(2)这50张车票可以看做共享资源;(3)3个售票窗口相当于3个线程。由于没有要求售票子线程返回结果,故既可以使用前面学习的类Thread来实现多窗口售票程序,也可以使用接口Runnable来实现多窗口售票程序。任务8.1多窗口售票程序设计任务8.1多窗口售票程序设计任务实现方法1:继承类Thread来实现多窗口售票程序Auto_Ticketing.java//自定义一个子线程类,继承自Thread类classTicketWindowextendsThread{privateinttickets=50;//构造方法,初始化子线程名publicTicketWindow(Stringname){super(name);}@Overridepublicvoidrun(){//重写方法run()while(tickets>0){System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}}}publicclassAuto_Ticketing{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow的实例对象1TicketWindowticketWindow1=newTicketWindow("售票窗口1");ticketWindow1.start();//启动线程//创建子线程类TicketWindow的实例对象2TicketWindowticketWindow2=newTicketWindow("售票窗口2");ticketWindow2.start();//启动线程//创建子线程类TicketWindow的实例对象3TicketWindowticketWindow3=newTicketWindow("售票窗口3");ticketWindow3.start();//启动线程}}存在问题:每个窗口各自销售50张票,运行结果如下图所示:任务8.1多窗口售票程序设计任务实现方法2:实现接口Runnable来实现多窗口售票程序Auto_Ticketing2.javaclassTicketWindow_RunableimplementsRunnable{privateinttickets=50;//实现接口方法run()publicvoidrun(){while(tickets>0){System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}}}
publicclassAuto_Ticketing2{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow_Runable的实例对象TicketWindow_RunableticketWindow=newTicketWindow_Runable();//创建三个子线程用于售票newThread(ticketWindow,"售票窗口1").start();newThread(ticketWindow,"售票窗口2").start();newThread(ticketWindow,"售票窗口3").start();}}任务8.1多窗口售票程序设计两种任务实现方式对比:
通过以上两行不同的实现售票程序的方式,可以看到通过实现Runnable接口方式相对于继承Thread方式实现多线程来说有一定的优势:(1)接口方式可以避免类的单继承带来的局限性;(2)接口方式更适合多个子线程处理共享资源的情况。实践训练按要求完成如下程序。(1)实现三个同学分吃10块蛋糕的应用;
(2)显示分吃过程,例如:“同学A分吃了蛋糕10”“同学B分吃了蛋糕9”等。任务8.1多窗口售票程序设计任务8.2优化多窗口售票程序任务2目标了解线程的生命周期及状态转换掌握线程的调度掌握多线程同步任务8.2优化多窗口售票程序任务2描述
在实际多窗口售票过程中,每个窗口售票都需要耗费一定的时间,所以每个售票窗口的线程是有延迟的。任务需要模拟线程的延迟,并确保在线程有延迟的情况下,售票可以正常进行,所售总票数修改为20张,运行效果如图8-7所示。图8-7任务2运行效果图任务8.2优化多窗口售票程序知识准备8.2.1概述线程的生命周期及状态转换Java线程的生命周期中包含6种状态,这六种状态存放在Thread类的内部类State中,线程状态之间的转换可以看图8-8所示。图8-8线程状态转换图任务8.2优化多窗口售票程序8.2.1概述线程的生命周期及状态转换下面对线程的6种状态做详细讲解。新建状态(NEW):当程序使用new关键字创建了一个线程对象后,该线程就处于新建状态,此时JVM会为其分配内存,并初始化对象的成员变量。可运行状态(RUNNABLE):当线程对象调用了start()方法之后,该线程就进入可运行状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。可运行状态又细分为两种状态:就绪状态(READY)和运行状态(RUNNING),线程根据能否获得CPU执行时间在就绪和运行状态间转换。就绪状态:线程对象调用了start()方法后,该线程并没有立刻运行,要等待JVM(Java虚拟机)的调度,此时就是就绪状态,相当于“等待执行”。运行状态:当线程对象获得JVM调度,也就是获得CPU使用权后即转换为运行状态,开始执行run()方法或call()方法的线程执行体。任务8.2优化多窗口售票程序8.2.1概述线程的生命周期及状态转换阻塞状态(BLOCKED):当处于运行状态的线程失去所占用资源之后,便停止运行进入阻塞状态。阻塞状态的线程只有先进入就绪状态,才能有机会转换进入运行状态。线程进入阻塞状态通常是由两种原因产生的:线程运行过程中,获取同步锁失败;线程运行过程中,发出I/O请求时;无限期等待状态(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程显式地唤醒。以下方法会让线程陷入无限期的等待状态:没有设置Timeout参数的Object.wait()方法没有设置Timeout参数的Object.join()方法任务8.2优化多窗口售票程序8.2.1概述线程的生命周期及状态转换限期等待状态(TIMED_WAITING):处于这种状态的线程也不会被分配CPU执行时间,不过无须等待其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒并进入可运行时状态的就绪状态。以下方法会让线程进入限期等待状态:设置了Timeout参数的Object.wait(longmillis)方法设置了Timeout参数的Object.sleep(longmillis)方法设置了Timeout参数的Object.join(longmillis)方法终止状态(TERMINATED):线程执行完了或者因异常退出了run()方法或call()方法,该线程就进入终止状态,生命周期结束。任务8.2优化多窗口售票程序8.2.2线程的调度
程序中的多个线程看起来是并发执行的,但实际上并一定是同一时刻执行。线程要执行,必须先获得CPU使用权,Java虚拟机会按照特定的机制为程序中的线程分配CPU的使用权,这种机制称为线程的调度。
线程调度有两种模型:分时调度模型:让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用的CPU时间片。抢占式调度模型:让可运行池中所有就绪状态的线程争抢CPU的使用权,高优先级的线程获取CPU使用权的概率大于低优先级的线程。下面围绕线程调度进行详细的讲解。任务8.2优化多窗口售票程序8.2.2.1线程的优先级
如果要对线程进行调度,最直接的方式是设置线程的优先级。优先级高的线程获得CPU使用权的机会大于优先级低的线程。
在Java中设置线程的优先级可使用Thread类的方法setPriority(intnewPriority),线程的优先级可以设置为1—10之间的整数,数字越大代表优先级越高;除了使用数字,Thread类还提供了三个优先级静态常量表示线程的优先级:MAX_PRIORITY=10,最高优先级MIN_PRIORITY=1,最低优先级NORM_PRIORITY=5,默认优先级
如果没有设置线程的优先级,则线程具有默认优先级,主线程默认优先级为5,如果A线程创建了B线程,那么B线程和A线程具有相同的优先级。任务8.2优化多窗口售票程序下面我们通过一个案例来查看不同优先级的线程执行情况,如例8-4Example8_4.java所示。publicclassExample8_4{publicstaticvoidmain(String[]args){//分别创建两个线程对象,用于循环输出语句Threadthread1=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":abcd");}}},"低优先级的线程");Threadthread2=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":1234");}}},"高优先级的线程");thread1.setPriority(Thread.MIN_PRIORITY);//设置线程优先级为最低级别thread2.setPriority(Thread.MAX_PRIORITY);//设置线程优先级为最高级别//分别开启两个线程thread1.start();thread2.start();}}运行结果如下图所示:任务8.2优化多窗口售票程序8.2.2.1线程的优先级
注意:
线程的优先级需要操作系统的支持,不同的操作系统支持的线程优先级不同的,不一定能和Java中支持的线程优先级对应。建议使用时使用前面讲述的三个常量优先级。另外,设计多线程应用时,不能只依赖于线程优先级来实现所需功能。任务8.2优化多窗口售票程序8.2.2.2线程的休眠
如果在线程执行过程中,要暂停正在执行的线程,让出CPU使用权,可以使用方法staticvoidsleep(longmillis)来设置让当前正在执行的线程暂停一段时间,并进入休眠等待状态,这样其他的线程可以得到执行的机会。sleep()方法会声明抛出InterruptedException异常,故在调用该方法时应捕获处理该异常或者是声明抛出该异常。下面我们通过一个案例来查看sleep()方法的使用情况,如例:8-5所示。例8-5Example8_5.java任务8.2优化多窗口售票程序publicclassExample8_5{publicstaticvoidmain(String[]args){//分别创建两个线程对象,用于循环输出语句Threadthread1=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":abcd"+i);if(i==1){try{//线程在执行过程睡眠500毫秒,则线程进入限时等待状态Thread.sleep(500);}catch(InterruptedExceptione){e.printStackTrace();}}}}},"线程1");Threadthread2=newThread(newRunnable(){@Overridepublicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":1234");}}},"线程2");//分别开启两个线程thread1.start();thread2.start();}}例8-5Example8_5.java运行结果如下图所示:任务8.2优化多窗口售票程序8.2.2.1线程的休眠注意:
当某线程调用sleep()方法后,该线程放弃CPU使用权,在指定的时间段内,该线程不会获得执行的机会。只有当休眠时间结束后,线程才会转换到就绪状态,等待再次获得CPU使用权执行。休眠状态下的线程不会释放同步锁/同步监听器。任务8.2优化多窗口售票程序8.2.2.3线程的让步
线程让步是通过调用方法yield()来实现,它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到“运行状态”继续运行。
下面我们通过一个案例来查看yield()方法的使用情况,如例:8-6所示。例8-6Example8_6.java任务8.2优化多窗口售票程序//自定义一个线程类,继承自Thread类classMyYieldThreadextendsThread{//构造方法,初始化线程名publicMyYieldThread(Stringname){super(name);}//重写run()方法,方法内是线程要执行的语句publicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":aaaa-"+i);if(i==1){System.out.println("线程让步:");Thread.yield();//线程暂行执行,做出让步}}}}publicclassExample8_6{publicstaticvoidmain(String[]args){//分别创建两个线程对象,用于循环输出语句MyYieldThreadmyYieldThread1=newMyYieldThread("myYieldThread1");MyYieldThreadmyYieldThread2=newMyYieldThread("myYieldThread2");//分别开启两个线程myYieldThread1.start();myYieldThread2.start();}}例8-6Example8_6.java运行结果如下图所示:任务8.2优化多窗口售票程序8.2.2.4线程的插队
线程的插队是通过调用join()方法来实现。当在某个线程中调用其他线程的join()方法,则调用的线程被阻塞,直到被join方法加入的线程执行完成后它才会继续执行。
下面我们通过一个案例来查看join()方法的使用情况,如例:8-7所示。例8-7Example8_7.java任务8.2优化多窗口售票程序//自定义一个线程类,继承自Thread类classMyJoinThreadextendsThread{//构造方法,初始化线程名publicMyJoinThread(Stringname){super(name);}//重写run()方法,方法内是线程要执行的语句publicvoidrun(){for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":aaaa-"+i);}}}publicclassExample8_7{publicstaticvoidmain(String[]args)throwsInterruptedException{//分别创建两个线程对象,用于循环输出语句MyJoinThreadmyJoinThread1=newMyJoinThread("myJoinThread1");myJoinThread1.start();for(inti=0;i<5;i++){System.out.println(Thread.currentThread().getName()+":bbbb-"+i);if(i==1){myJoinThread1.join();//线程插队}}}}例8-7Example8_7.java运行结果如下图所示:任务8.2优化多窗口售票程序8.2.3多线程同步
多线程并发执行可以提高程序的效率,但是当多个线程访问同一共享资源时,也会引发安全问题。8.2.3.1线程安全
例如:前面的售票程序,在实际售票的时候,每个售票窗口在出售票的时候都是有一定耗时的,则在耗时过程中可能会导致同一张票被多个窗口销售,也可能会出现被销售多出20张的票。
下面我们通过修改前面的任务1的程序Auto_Ticketing2查看线程安全问题,修改后的程序为:模拟售票有耗时的情况Auto_Ticketing3.java任务8.2优化多窗口售票程序classTicketWindow3_RunableimplementsRunnable{privateinttickets=20;//实现接口方法run()publicvoidrun(){while(tickets>0){try{Thread.sleep(1000);//模拟售票等待过程}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}}}
publicclassAuto_Ticketing3{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow_Runable的实例对象TicketWindow3_RunableticketWindow=newTicketWindow3_Runable();//创建三个子线程用于售票newThread(ticketWindow,"售票窗口1").start();newThread(ticketWindow,"售票窗口2").start();newThread(ticketWindow,"售票窗口3").start();}}模拟售票有耗时的情况:Auto_Ticketing3.java运行结果如下图所示:任务8.2优化多窗口售票程序8.2.3.1线程安全说明:
从上面运行结果可以看到在多线程售票中出现了安全问题:由于线程有延迟,售票窗口3正在销售第6张票,此时共享资源的剩余票是6张,如果在售票窗口1此时获得了CPU使用权,发现票数是6张,也会销售第6张票。同理,当售票窗口正在销售最后1张票时,在线程延迟中,其他售票窗口线程也可能会获得CPU使用权,则判断时发现剩余票数是1张,大于0,则仍然可以继续售票,就可能会出现销售第0,第-1张票的情况。
从上述程序运行结果可以看到,线程安全问题主要是由多个线程同时处理共享资源造成的。如果要解决这个安全问题,须要保证在同一时刻只能有一个线程访问共享资源。Java中提供了线程同步机制来解决线程安全问题。任务8.2优化多窗口售票程序8.2.3.2线程同步
Java中提供了三种线程同步方式来解决线程安全问题:一种是同步代码块,一种是同步方法,还有一种是同步锁。(1)同步代码块
同步代码块,是当多个线程使用同一个共享资源时,将处理共享资源的代码放置在关键字synchronized修饰的代码块中,这段代码被称为同步代码块。其语法格式如下:synchronized(lock){ //需要同步操作的代码}任务8.2优化多窗口售票程序8.2.3.2线程同步(1)同步代码块
说明:
同步代码块格式中,synchronized代表同步,lock是一个同步锁,可以是任意类型的对象,但是多个线程共享的同步锁对象必须是相同的,且在任何时候,最多允许使用共享资源的多个线程中的一个线程拥有同步锁,谁获得同步锁就可以进入执行代码块。
当线程A执行同步代码块时,首先检查同步锁对象的标志位,默认情况下标志位为1,此时线程A可以执行同步代码块,同时将所对象的标志位置为0。当另外一个线程B执行到同步代码快时,由于锁对象标志位为0,线程B阻塞,等待当前线程A执行完同步代码块后,把锁对象标志位置为1,线程B才能执行同步代码块。任务8.2优化多窗口售票程序8.2.3.2线程同步(2)同步方法
在一个方法前面加上关键字synchronized进行修饰,被修饰的方法称为同步方法。语法格式如下:
说明:
同步方法在同一时刻只允许一个线程访问,访问该方法的其他线程都会被阻塞,直到当前线程访问执行完毕后,其他线程才有机会访问执行。[修饰符]synchronized返回值类型方法名([参数1,…]){ //需要同步操作的代码}任务8.2优化多窗口售票程序8.2.3.2线程同步(3)同步锁
同步代码块和同步方法使用的是封闭式锁机制,它无法中断一个正在等候获得锁的线程,也无法通过轮询得到锁。每个线程在执行同步代买的时候,每次都需要判断锁的状态,资源消耗比较大,性能比不用要低一些。故在编程时建议尽量减少synchronized的作用域。
从JDK1.5开始,Java提供了比同步代码块和同步方法更广泛的锁操作,是一个功能更强大的Lock锁,既具有同步代码块和同步方法的功能,同时在使用时也更灵活,且可以让线程释放锁。
任务8.2优化多窗口售票程序8.2.3.2线程同步(3)同步锁
同步锁Lock是一个接口,它的实现类是ReentrantLock。在编程中最常用的是创建一个同步锁对象,对代码块进行上锁和解锁操作。如下所示:说明:上锁使用的是方法lock(),解锁使用的是方法unlock(),可以在需要的位置灵活的上锁解锁。除此之外,ReentrantLock类还提供的有其他对锁的操作方法。privatefinalLocklock=newReentrantLock();//创建同步锁对象……lock.lock();//上锁 //需要同步操作的代码lock.unlock();//解锁……任务实施根据任务分析可知:(1)模拟售票窗口耗时可以使用前面学习的线程休眠方法sleep()来实现;(2)为确保线程安全,既可以使用前面学习的同步代码块来实现,也可以使用同步方法来实现多窗口售票程序,当然也可以使用同步锁来实现线程安全的售票。任务8.2优化多窗口售票程序任务8.2优化多窗口售票程序//创建类实现接口RunnableclassTicketWindow4_RunableimplementsRunnable{privateinttickets=20;Objectlock=newObject();//实现接口方法run()publicvoidrun(){while(true){synchronized(lock){if(tickets>0){try{Thread.sleep(1000);//模拟售票等待过程}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}}}}}
publicclassAuto_Ticketing4{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow_Runable的实例对象TicketWindow4_RunableticketWindow=newTicketWindow4_Runable();//创建三个子线程用于售票newThread(ticketWindow,"售票窗口1").start();newThread(ticketWindow,"售票窗口2").start();newThread(ticketWindow,"售票窗口3").start();}}任务实现方法1:同步代码块Auto_Ticketing4.java运行结果如下图所示:任务8.2优化多窗口售票程序//创建类实现接口RunnableclassTicketWindow5_RunableimplementsRunnable{privateinttickets=20;//实现接口方法run()publicvoidrun(){while(true){saleTicket();}}privatesynchronizedvoidsaleTicket(){if(tickets>0){try{Thread.sleep(1000);//模拟售票等待过程}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}}}
publicclassAuto_Ticketing5{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow_Runable的实例对象TicketWindow5_RunableticketWindow=newTicketWindow5_Runable();//创建三个子线程用于售票newThread(ticketWindow,"售票窗口1").start();newThread(ticketWindow,"售票窗口2").start();newThread(ticketWindow,"售票窗口3").start();}}任务实现方法2:同步方法之Auto_Ticketing5.java运行结果如下图所示:任务8.2优化多窗口售票程序importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;//创建类实现接口RunnableclassTicketWindow6_RunableimplementsRunnable{privateinttickets=20;privatefinalLocklock=newReentrantLock();//实现接口方法run()publicvoidrun(){while(true){lock.lock();if(tickets>0){try{Thread.sleep(1000);//模拟售票等待过程System.out.println(Thread.currentThread().getName()+"正在销售第"+tickets--+"张票");}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}}}}}
publicclassAuto_Ticketing6{publicstaticvoidmain(String[]args){//创建子线程类TicketWindow_Runable的实例对象TicketWindow6_RunableticketWindow=newTicketWindow6_Runable();//创建三个子线程用于售票newThread(ticketWindow,"售票窗口1").start();newThread(ticketWindow,"售票窗口2").start();newThread(ticketWindow,"售票窗口3").start();}}任务实现方法3:同步锁之Auto_Ticketing6.java
运行结果如下图所示:任务8.2优化多窗口售票程序三种任务实现方式对比:
从上述运三种实现方法可以看到任何一种方法都可以实现任务需求。编程过程中可以根据需要确定使用同步代码块/同步方法/同步锁的任意一种来实现线程安全需要。同步锁会更灵活,功能更强大。实践训练按要求完成如下程序。(1)实现三个同学分吃10块蛋糕的应用;
(2)需要模拟分吃的过程的延迟,且确保线程安全;(3)显示分吃过程,例如:“同学A分吃了蛋糕10”“同学B分吃了蛋糕9”等。任务8.2优化多窗口售票程序任务8.3模拟“生产-消费”程序设计任务3目标掌握线程的等待和唤醒了解多线程通信任务8.3模拟“生产-消费”程序设计任务3描述在现实生活中,生产者负责生产商品,消费者负责消费商品,当生产者生产商品后,消费者才能消费商品。运行效果如图8-15所示。图8-15任务3运行效果图任务8.3模拟“生产-消费”程序设计知识准备多线程通信 Java中不同的线程执行不同的任务,如果任务之间有某种关系,线程间必须能够通信,协调完成工作。为了让线程间能进行协调工作,就需要线程间能进行通信。Java提供了线程间通信常用的三个方法是:wait(),nofity(),notifyAll(),用于线程的等待与唤醒。voidwait(),让当前线程放弃同步锁并进入等待状态,直到其他线程进入此同步锁,并调用nofity()方法或notifyAll()方法唤醒该线程为止。wait(longtimeout),让当前线程放弃同步锁并进入等待状态,直到其他线程调进入此同步锁,并调用notify()方法或notifyAll()方法,或者超过指定的时间量,当前线程被唤醒进入就绪状态。notify(),唤醒在此同步锁上等待的单个线程。notifyAll(),唤醒在此同步锁上等待的所有线程。任务实施根据任务分析可知:(1)生产者和消费者操作的是共享资源,需要定义共享资源类,并在共享资源类中定义生产和取出数据的方法;(2)生产者需要定义一个线程类;(3)消费者需要定义一个线程类;(4)编写测试类分别调用生产者和消费者实现生产消费交替完成。任务8.3模拟“生产-消费”程序设计任务8.3模拟“生产-消费”程序设计//共享资源类classShareResource{privateStringgoods_No;privateStringgoods_Name;//生产存入数据publicvoidpush(Stringgoods_No,Stringgoods_Name){this.goods_No=goods_No;try{Thread.sleep(20);//模拟网络延迟}catch(InterruptedExceptione){e.printStackTrace();}this.goods_Name=goods_Name;System.out.println("生产者生产:"+this.goods_No+"-"+this.goods_Name);}//消费取出数据publicvoidpopup(){try{Thread.sleep(20);//模拟网络延迟}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("消费者消费:"+this.goods_No+"-"+this.goods_Name);}}任务实现方法1:
Productive_Consumption.java任务8.3模拟“生产-消费”程序设计//生产者线程类classProducerimplementsRunnable{privateShareResourceshareResource;publicProducer(ShareResourceshareResource){this.shareResource=shareResource;}@Overridepublicvoidrun(){for(inti=1;i<=10;i++){if(i%2==0)//当循环变量奇偶数不同生产的数据也不同shareResource.push("货物编号"+i,"货物名称"+i);elseshareResource.push("商品编号"+i,"商品名称"+i);}}}任务实现方法1:
Productive_Consumption.java任务8.3模拟“生产-消费”程序设计//消费者线程类classConsumerimplementsRunnable{privateShareResourceshareResource;publicConsumer(ShareResourceshareResource){this.shareResource=shareResource;}@Overridepublicvoidrun(){for(inti=1;i<=10;i++){shareResource.popup();//消费取出数据}}}任务实现方法1:
Productive_Consump
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度节能工厂租赁合同编制要则3篇
- 二零二五年度飞机销售合同附带飞行员培训及考核协议3篇
- 二零二五年度公寓装修及设施配套合同3篇
- 二零二五版出口货物安全检验合同规定与流程3篇
- 二零二五年度汽车租赁合同解除与终止范本汇编3篇
- 二零二五版汽车维修担保书之担保函与担保合同3篇
- 二零二五版别墅窗帘设计、安装及智能家居集成合同3篇
- 二零二五年度高级管家雇佣合同范本3篇
- 二零二五年度餐饮连锁企业股权合作合同范本2篇
- 二零二五版个人二手车维修借款担保合同3篇
- 第三十六届全国电力行业风力发电运行检修职业技能竞赛基础理论题库附有答案
- 2024年纪检监察综合业务知识题库含答案(研优卷)
- 科室医疗质量与安全管理小组工作制度
- 中华民族共同体概论课件第五讲大一统与中华民族共同体初步形成(秦汉时期)
- 初二生地会考试卷及答案-文档
- 私营企业廉洁培训课件
- 施工单位值班人员安全交底和要求
- 中国保险用户需求趋势洞察报告
- 数字化转型指南 星展银行如何成为“全球最佳银行”
- 中餐烹饪技法大全
- 灵芝孢子油减毒作用课件
评论
0/150
提交评论