第2章-Java多线程应用ppt课件(全).ppt
第第2章章 Java多线程应用多线程应用12.1 线程和多线程2.2 实例1 Java程序的多线程机制 2.3 实例2 Java程序中的多线程实现 2.4 实例3 基于Java语言的多线程同步机制2.5实例4 用Java语言实现经典的同步互斥问题 22.1 线程和多线程线程和多线程线程(thread)是指计算机正在执行的程序中的一个控制流程。线程本身不是完整程序,没有执行的入口,也没有出口,因此其自身不能自动运行,而必须栖身于某一进程之中,由进程触发执行。Thread类提供了sleep()、join()、interrupt()、currentThread()、isAlive()和stop()等方法,用来控制和协调线程。下面介绍几个常用的方法。1.sleep()sleep()方法使一个线程暂停运行一段固定的时间。在休眠时间内,线程将停止运行。休眠时间的长短由sleep()方法的参数决定。public void run()/进行其他处理后Thread.sleep(200);/休眠时间以ms为单位32.join()join()方法使当前正在执行的线程进入等待状态(挂起),直至方法join()所调用的线程结束。设已经生成了一个线程zsmthread,而当前正在运行的另一线程中执行了方法outtime():public void outtime()/挂起线程,并运行线程zsmthreadzsmthread.join();/继续执行本线程其余的语句这样,在执行方法outtime()之后,现行的线程被挂起,转向运行线程zsmthread,直到线程zsmthread运行结束,才继续执行本线程剩下的语句。3.同步问题在多线程应用程序中,线程是共享内存及其他资源的,但各个线程对这些数据的访问却是异步的,即当某一个线程访问共享数据时,并不知道其他线程是否也在访问这些数据。因此,有时会出现共享数据不一致的危险。这就是多线程的同步问题。在Java语言中,解决线程同步的基本思想是:避免多个线程访问同一个对象,方法是为每一个对象的实例做一个标志,这个标志称为“锁定标志”。关键字synchronized提供了操作这个标志的方法,这样,保证在任何时刻只有一个线程在访问共享数据。42.2 实例实例1 Java程序的多线程机制程序的多线程机制Java程序通过流控制来执行程序流,每个单个的流控制称为一个线程。如果一个程序中可有多个线程同时执行不同的任务,则称为多线程。多线程程序有提高系统的输入/输出速度、有效利用系统资源、改善计算机通信功能等优点。2.2.1 线程的生命周期一个线程创建之后,总处于某种状态之中,线程的状态表示了线程正在进行的活动以及在这段时间内线程能完成的任务,在线程的生命周期中有四种状态,通过对线程进行操作来改变其状态。1.创建状态创建了一个线程而还没有启动它,则处于创建状态,此时仅是一个空的线程对象,并不获得应有资源,只有启动后,系统才为它分配资源。处于创建状态的线程可以进行两种操作:一是通过调用start()方法启动,使其进入可运行状态;二是调用stop()方法,使其进入消亡状态。2.可运行状态在线程的创建状态中进行启动操作,则此线程进入可运行状态。可运行状态只说明该线程具备了运行的条件,但并不一定是运行状态,因为在单处理器系统中运行多线程程序,实际上在每个“时刻”至多有一个线程在运行,而系统中可能有多个线程都处于运行状态,系统通过快速切换和调度使所有可运行的线程共享处理器,造成宏观上的多线程并发运行。在可运行状态,线程运行的是线程体,线程体由run()方法规定,在自己定义的线程类中重写。在可运行状态下可进行多种操作:调用suspend()方法,使线程挂起,从而进入不可运行状态;调用sleep()方法,使线侱睡眠,从而进入不可运行状态;调用wait()方法,使线程等待,从而进入不可运行状态;调用yield()方法,使线程退让,使线程把CPU控制权提前交给同级优先权的其他线程;调用stop()方法,使线程终止,从而进入消亡状态。正常的情况下是执行完run()方法,使线程结束,进入消亡状态。53.不可运行状态不可运行状态从可运行状态转换而来,一个处于可运行状态的线程遇到下列情况进入不可运行状态:调用了suspend()方法;调用了sleep()方法;调用了wait()方法;另外,如果一个线程是和I/O操作有关的,则在执行I/O指令时,由于外设速度远远低于CPU速度而使线程受到阻塞,此时也可进入不可运行状态。当线程处于不可运行状态时,可通过下面途径恢复到可运行状态:通过sleep()方法进入不可运行状态的线程,在过了指定的睡眠时间后自动恢复;由于I/O阻塞而进入不可运行状态的线程在外设完成I/O操作后自动恢复;通过suspend()方法进入不可运行状态的线程,通过调用resume()方法恢复;通过wait()方法进入不可运行状态的线程,通过调用notify()或notifyAll()方法恢复。采用等待操作往往是由于线程要等待某个条件变量,当获得此条件变量后,便可调用此两方法恢复,一般用于多线程的同步机制中。在不可等待状态,也可调用stop()方法,使进入消亡状态。4.消亡状态有两种情况使线程进入消亡状态:从可运行状态执行完run()方法,自然撤消;从任何一种其他状态调用stop()方法。从消亡状态不能转换到别的状态,因为到此线程的生命周期就结束了。62.3.1 为什么会排队等待?为什么会排队等待?下面的这个简单的Java程序完成四项不相关的任务。这样的程序有单个控制线程,控制在这四个任务之间线性的移动。此外,因为所需的资源打印机、磁盘、数据库和显示屏都有内在的潜伏时间,所以每项任务都包含明显的等待时间。因此,程序在访问数据库之前必须等待打印机完成打印文件的任务。如果您正在等待程序的完成,则这是对计算机资源和您的时间的一种拙劣使用。改进此程序的一种方法是使它成为多线程的。2.3 实例实例2 Java程序中的多线程实现程序中的多线程实现7Java编程语言使多线程简单有效,main()函数也是一个线程。程序员只有在需要多个线程时才需要创建新的线程。1.Thread类Thread类是一个具体的类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从Thread类导出的新类。必须覆盖Thread的run()函数来完成有用的工作。用户并有直接调用此函数;而是必须调用Thread的start()函数,该函数再调用run()的代码。2.3.2 Java编程语言实现多线程编程语言实现多线程8此接口只有一个此接口只有一个run()函数,此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有函数,此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用不同。我们可以用runnable接口改写前一个示例。接口改写前一个示例。创建两个新线程而有强加类层次:创建两个新线程而有强加类层次:import java.util.*;class TimePrinter implements Runnable int pause Time;String name;public TimePrinter(int x,String n)pauseTime=x;name=n;public void run()while(true)try System.out.printin(name+:+newDate(System.xurrentTimeMillis();Thread.sleep(pauseTime);catch(Exception e)System.out.println(e);static public void main(String args)Thread t1=new Thread(new TimePrinter(1000,Fast Guy);t1.start();Thread t2=new Thread(new TimePrinter(3000,Slow Guy);t2.start();请注意,当使用请注意,当使用runnable接口时,不能直接创建所需类的对象并运行它,必须从接口时,不能直接创建所需类的对象并运行它,必须从Thread类的一个实例内部运行它。由于类的一个实例内部运行它。由于从从Thread类继承会强加类层次,许多程序员更喜欢类继承会强加类层次,许多程序员更喜欢runnable接口。接口。2.Runnable接口接口9Java语言的重要特性之一就是通过内置的多线程机制来支持并发程序的设计,从而达到提高程序执行性能的目的.与此同时,并发程序的设计必须解决多方面的问题,例如如何避免全局资源共享可能带来的危险,操作系统如何最佳地管理资源的分配,如何定位程序设计的正确性等。为此,我们必须引入行之有效的线程同步机制,使并发执行的诸线程之间能够有效地共享资源和相互合作,从而使程序的执行具有可再现性。Java语言的内置多线程模型为并发程序的设计提供了较为完善的线程同步机制,可以很好地解决并发程序设计中的问题。2.4 实例实例3 基于基于Java语言的多线程同步机制语言的多线程同步机制102.5 实例实例4 用用Java语言实现经典的同步语言实现经典的同步互斥问题互斥问题线程(线程(Thread)简介)简介1.线程和进程线程和进程所谓进程是指在系统中正在运行的一个应用程序所谓进程是指在系统中正在运行的一个应用程序;所谓线所谓线程是系统分配处理器时间资源的基本单元,或者说是进程程是系统分配处理器时间资源的基本单元,或者说是进程之内独立执行的一个单元。在一个多任务多线程的操作系之内独立执行的一个单元。在一个多任务多线程的操作系统中,每一个应用程序都是一个进程(统中,每一个应用程序都是一个进程(Process)。进程)。进程可以创建多个并发的线程(可以创建多个并发的线程(Thread),同时进程也以主),同时进程也以主线程(线程(PrimaryThread)的形式被系统调度。对于操作系)的形式被系统调度。对于操作系统而言,系统调度单元是线程。一个进程至少包括一个线统而言,系统调度单元是线程。一个进程至少包括一个线程,通常将该线程称为主线程。一个进程从主线程的执行程,通常将该线程称为主线程。一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务的多任务 112.5.2 生产者生产者消费者问题及程序实现消费者问题及程序实现1.问题描述 假定缓冲池中有n个缓冲区,每个缓冲区存放一个产品。一群生产者p1,p2,pm和一群消费者线程c1,c2,cm,它们的工作分别是:只要缓冲池未满,生产者立即把产品送入缓冲区,类似地,只要缓冲未空,消费者便可从缓冲区中取走产品来消费。生产者和消费者的同步关系是禁止向满的缓冲池中输送产品,也禁止消费者从空的缓冲池中提取产品。如图2-3所示。122.5.3 哲学家进餐问题及程序实现哲学家进餐问题及程序实现1.问题描述问题描述 有五个哲学家,他们的生活方式是交替地有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共同一张圆桌,进行思考和进餐,哲学家们共同一张圆桌,周围放有五张椅子,每人坐一张。在圆桌周围放有五张椅子,每人坐一张。在圆桌上有五碗米饭和五只筷子,当哲学家思考上有五碗米饭和五只筷子,当哲学家思考时,他们不同同事交谈,饥饿时便试图取时,他们不同同事交谈,饥饿时便试图取其左、右最靠近他们的筷子,但是他们可其左、右最靠近他们的筷子,但是他们可能一只也拿不到。只有在他们拿到两只筷能一只也拿不到。只有在他们拿到两只筷子时方能进餐,餐毕,放下筷子,又继续子时方能进餐,餐毕,放下筷子,又继续思考。思考。13(1)哲学家。)哲学家。public class philosopher extends Thread String philo_name;/哲学家名哲学家名 fork tableware;/fork类实例类实例 int left,right;/左右手筷子左右手筷子 public philosopher(String name,fork set,int left_hand,int right_hand)philo_name=name;tableware=set;left=left_hand;right=right_hand;public void run()while(true)/得到左手边的筷子得到左手边的筷子tableware.get_fork(philo_name,left,left_hand);try/睡眠一随机时间,让出处理器睡眠一随机时间,让出处理器 Thread.sleep(int)(Math.random()*50);catch(InterruptedException e)/得到右手边的筷子得到右手边的筷子tableware.get_fork(philo_name,right,right_hand);/有一个哲学家正在吃有一个哲学家正在吃 System.out.println(philo_name+is eating.);try/睡眠一随机时间,让出处理器睡眠一随机时间,让出处理器 Thread.sleep(int)(Math.random()*5000);catch(InterruptedException e)tableware.put_fork(philo_name,left,left_hand);/吃完后放下左右手上的筷子吃完后放下左右手上的筷子tableware.put_fork(philo_name,right,right_hand);try Thread.sleep(int)(Math.random()*1000);catch(InterruptedException e)14(2)筷子。筷子。import java.applet.*;public class fork boolean buf;public fork()buf=new Boolean5;for(int I=0;I 5;I+)bufi=true;synchronized public void put_fork(String name,int index,String hand)bufindex=true;System.out.println(name+put down his+hand+forks);/放下筷子放下筷子 notify();/唤醒其他等待线程唤醒其他等待线程 public synchronized void get_fork(String name,int index,String hand)/得到筷子,否则等待得到筷子,否则等待 while(!bufindex)try System.out.println(name+is waiting for the+hand+forks);wait();/得不到所需的筷子,转入等待队列得不到所需的筷子,转入等待队列 catch(InterruptedException e)bufindex=false;System.out.println(name+get his+hand+forks);15(3)主程序。主程序。public class Test public static void main(String args)philosopher philo;fork tableware;Thread myThread;phile=new philosopher5;tableware=new fork();/创建创建fork实例实例 for(int I=0;i4;i+)philoi=new philosopher(philosopher+i,tableware,i+1,i)philo4=new philosopher(philosopher+4,tableware,0,4);myThread=new Thread5;for(int i=0;i5;i+)myThreadi=new Thread(philoi);myThreadi.setPriority(Thread.MIN_PRIORITY);/创建线程,给这些线程赋予不同的优先级创建线程,给这些线程赋予不同的优先级myThread0.setPriority(Thread.MAX_PRIORITY)for(int i=0;i5;i+)myThreadi.start();16