《Java应用开发与实践-第10章Java多线程.ppt》由会员分享,可在线阅读,更多相关《Java应用开发与实践-第10章Java多线程.ppt(39页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、l第第10章章 Java多线程多线程Java应用开发与实践第第二二部分部分 酒店管理系统的设计酒店管理系统的设计学习目标学习目标l理解进程和线程的区别和联系理解进程和线程的区别和联系l掌握线程的创建方法掌握线程的创建方法l了解线程同步的概念了解线程同步的概念l了解线程调度的概念了解线程调度的概念n10.1 进程和线程进程和线程n10.2 线程的创建线程的创建n10.3 线程同步线程同步n10.4 线程调度线程调度n10.5 实训实训10 多线程的练习和应用多线程的练习和应用目录目录10.1 进程和线程进程和线程l进程是一个在内存中运行的应用程序。每个进程都有独立的代码和数据空间(进程上下文),
2、有它特定的进程号。他们共享系统的内存资源,进程间的切换会有较大的开销,一个进程包含若干个线程,进程是资源分配的最小单位。l线程是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程共享进程的堆和方法区资源,同时每个线程有独立的运行栈和程序计数器(PC),线程切换开销小,因此线程也被称为轻量级进程。线程是CPU调度的最小单位。l由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Java也不例外。10.1.1 认识进程和线程认识进程和线程10.1 进程和线程进程和线程1)多线程技术使程序的响应速度更快,因为
3、用户界面可以在进行其它工作的同时一直处于活动状态;2)当前没有进行处理的任务时可以将处理器时间让给其它任务;3)占用大量处理时间的任务可以定期将处理器时间让给其它任务;4)可以随时停止任务;5)可以分别设置各个任务的优先级以优化系统的性能。在以下情况下,最适合采用多线程处理:1)耗时或大量占用处理器的任务阻塞用户界面操作;2)各个任务必须等待外部资源(如远程文件或 Internet连接)。10.1.2 多线程的特点多线程的特点10.1 进程和线程进程和线程在Java中任何对象都有生命周期,线程也不例外,线程的创建即是线程的生命周期的开始,当run()方法执行完毕或者线程抛出一个未捕获的异常或者
4、错误的时候,线程死亡。因此线程从创建到死亡的过程就是一个动态执行的过程。10.1.3 线程的生命周期及五种基本状态线程的生命周期及五种基本状态10.1 进程和线程进程和线程10.1.3 线程的生命周期及五种基本状态线程的生命周期及五种基本状态10.1 进程和线程进程和线程l新建状态(new):当线程对象对创建后,即进入了新建状态,如:Thread t=new MyThread();l就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start(
5、)此线程立即就会执行;l运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。就绪状态是进入到运行状态的唯一入口。也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;10.1.3 线程的生命周期及五种基本状态线程的生命周期及五种基本状态10.1 进程和线程进程和线程l阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。l死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。1
6、0.1.3 线程的生命周期及五种基本状态线程的生命周期及五种基本状态10.2 线程的创建线程的创建lThread类是java.lang包下的一个常用类,每一个Thread类的对象,就代表一个处于某种状态的线程。lThread类用于操作线程,是所有涉及到线程的操作(如并发)的基础。创建线程比较常用的一种方法就是继承Thread类。l如果应用系统只需要建立一条线程,而没有什么其它特殊的要求,那么继承Thread类无疑是较好的选择。lThread类中的方法可分为实例方法和静态方法,其中实例方法有start()方法,run()方法等,静态方法有currentThread()方法,sleep(long
7、millis)方法等。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建1.start()方法start()用来启动一个线程,当调用start()方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。要注意,调用start方法的顺序不代表线程启动的顺序,也就是CPU执行哪个线程的代码具有不确定性。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建2.run()方法这个方法是线程类调用start()后所执行的方法,如果直接调用run()而不是start()方法,那么和
8、普通方法一样没有区别。要注意,run()方法是不需要用户来调用的,当通过start()方法启动一个线程之后,当线程获得了CPU执行时间,便进入run()方法体去执行具体的任务。用start()方法来启动线程,是真正实现了多线程,通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法,无需等待run()方法执行完毕,即可继续执行下面的代码。所以说:start()方法是真正实现了多线程,run()方法只是一个普通的方法。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线
9、程的创建3.interrupt()方法使用这个方法不会中断线程。实际上调用interrupt实际作用是在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞状态。4.join()方法join方法会使得调用join方法的线程无限阻塞,直到调用join方法的线程销毁为止,join方法内部使用的是wait(),所以会释放锁。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建5.sleep(long millis)方法sleep()方法是静态方法,它的作用就是在指定的时间(单位是毫秒)让正在执行的线程休眠,不释放锁。后面小节有详述。6.yield()方法暂停
10、当前执行的线程对象,并执行其他线程。这个暂停会放弃CPU资源,放弃的时间不确定。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建继承Thread类的步骤如下:1)定义类继承Thread类;2)重写Thread类中的run()方法;重写run()方法的目的是将需要该线程执行的代码存储在run()方法运行。3)调用线程的start方法。该方法有两步:启动线程,调用run()方法。10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建class MyThread extends Thread public void r
11、un()for(int i=0;i 10;i+)try Thread.sleep(1000);catch(InterruptedException e)e.printStackTrace();/this.getName()获取当前线程System.out.println(this.getName();10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建public class Demo10_1 public static void main(String args)MyThread thead1=new MyThread();MyThread thead
12、2=new MyThread();thead1.start();thead2.start();10.2.1 通过继承通过继承Thread类创建线程类创建线程10.2 线程的创建线程的创建实现Runnable接口的步骤如下:1)定义类实现Runnable接口;2)实现Runnable接口中的run()方法,将线程要运行的代码放在该run()方法中;3)通过Thread类建立线程对象;4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。自定义的run()方法所属的对象是Runnable接口的子类对象。5)调用Thread类的start()方法开启线程并调用Runnabl
13、e接口子类的run()方法。10.2.2 通过实现通过实现Runnable接口创建线程接口创建线程10.2 线程的创建线程的创建class MyThread implements Runnable public void run()for(int i=0;i 10;i+)try Thread.sleep(1000);catch(InterruptedException e)e.printStackTrace();/Thread.currentThread()获取当前线程System.out.println(Thread.currentThread().getName();10.2.2 通过实现
14、通过实现Runnable接口创建线程接口创建线程10.2 线程的创建线程的创建public class Demo10_2 public static void main(String args)MyThread thread1=new MyThread();MyThread thread2=new MyThread();Thread th1=new Thread(thread1,MyThread1);Thread th2=new Thread(thread2,MyThread2);th1.start();th2.start();10.2.2 通过实现通过实现Runnable接口创建线程接口创建
15、线程10.2 线程的创建线程的创建l继承Thread类和实现Runnable接口都可以创建线程。在继承Thread的方式中,线程代码是存放在Thread子类run()方法中,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。优点是编写简单,可直接用this.getname()获取当前线程,不必使用Thread.currentThread()方法。但缺点也有,就是已经继承了Thread类后,无法再继承其他类。l实现Runnable接口的方式较为常用,实现Runnable接口的方式,更加的符合面向对象的思想:线程分为两部分,一部分线程对象,一部分线程任务
16、。将线程任务单独分离出来封装成对象,存放在接口的子类的run()方法中,类型就是Runnable接口类型。此方式优点是避免了单继承的局限性、多个线程可以共享一个target对象,非常适合多线程处理同一份资源的情形。缺点是比较复杂、访问线程必须使用Thread.currentThread()方法、无返回值。10.2.3 继承继承Thread类和实现类和实现Runnable接口的区别接口的区别10.3 线程同步线程同步l线程同步就是指各个线程协同步调,按预定的先后次序进行运行。这里的“同”字意思就是指协同、协助、互相配合。l多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序
17、(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的。l线程同步的主要任务是使并发执行的各线程之间能够有效的共享资源和相互合作,从而使程序的执行具有可再现性。l通常,在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。10.3.1 线程同步线程同步10.3 线程同步线程同步l线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者
18、释放该资源。l线程互斥可以看成是一种特殊的线程同步。所以,可以看出,在多个线程之间都需要访问共享资源(shared resource)的时候就会出现互斥现象。10.3.2 线程互斥线程互斥10.3 线程同步线程同步l线程同步的机制有临界区、互斥量、事件、信号量四种方式。1.临界区:在一段时间内只允许一个线程访问的资源就称为临界资源或独占资源,计算机中大多数物理设备,进程中的共享变量等待都是临界资源,它们要求被互斥的访问。每个进程中访问临界资源的代码称为临界区。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,
19、并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。10.3.3 线程同步机制线程同步机制10.3 线程同步线程同步l线程同步的机制有临界区、互斥量、事件、信号量四种方式。2、互斥量:互斥量和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。10.3.3 线程同步机制线程同步机制10.3 线程同步线程同步l线程同步的机制有临界区、互斥量、事件、信号量四种方式。3.信号量:信号量是维护0到指定最大值之间的同步对象
20、。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。4.事件:通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。10.3.3 线程同步机制线程同步机制10.4 线程调度线程调度l线程可以划分优先级,线程的优先级告诉CPU该线程的重要程度有多大。每个线程都具有优先级,Java 虚拟机根据线程的优先级决定线程的执行顺序。l程序会尽可能地先运行优先级高的那个程序,这样使多线程合理共享 CPU 资源而不会产生冲突。l通常优先级高的线程得到的CPU资源较多,也是CPU优先执行优先级较高的线程对象中的任务。l程序
21、尽可能运行优先级高的程序,并不意味着优先级较低的线程绝对不会运行。若程序的优先级较低,只不过表示它被允许的运行的几率小一些而已。lJava中的线程优先级分为1(Thread.MIN _PRIORITY)-10(Thread.MAX_PRIORITY),数字越大,优先级越高10.4.1 线程优先级的设置线程优先级的设置10.4 线程调度线程调度l线程休眠通常是调用Thread.sleep(毫秒数)方法,让当前运行的线程进入TIMED_WAITING(sleeping)阻塞状态,该方法使当前线程进入休眠状态,直到休眠设置的毫秒数后由系统唤醒,sleep方法上有一个异常。如果打断休眠就会抛出这个异常
22、。public static void sleep(long mills)throws InterruptedException10.4.2 线程休眠线程休眠10.4 线程调度线程调度lJava允许多线程并发控制,当多个线程同时操作一个可共享资源变量时(如对其进行增删改查操作),会导致数据不准确,而且相互之间产生冲突。l加入同步锁以避免该线程在没有完成操作前被其他线程调用,从而保证该变量的唯一性和准确性。在程序中需要完成下面两个操作:1)把竞争访问的资源标识为private;2)同步那些修改变量的代码,使用synchronized关键字同步方法或代码。10.4.3 线程同步线程同步10.4 线
23、程调度线程调度l从本质上说,synchronized是一种锁机制,它是为一个对象或者一个类标明一个锁,当线程想要执行相应的synchronized修饰的代码块时,它需要获得synchronized修饰的对象或者类的锁,这个就是CPU的使用权。只有拿到了锁才可以被CPU调度,获得处理权。当synchronized代码块执行结束后,这个线程就要释放相应的锁。CPU可以把这个锁分给其它的线程。lsynchronized可以作为函数的修饰符,也可作为函数内的语句,即同步函数或同步代码块来实现线程同步,因此它们都是同步锁。10.4.3 线程同步线程同步10.4 线程调度线程调度1.join()方法在很多
24、情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。join()方法主要作用是挂起(即插队)。10.4.4 线程常用方法线程常用方法10.4 线程调度线程调度2.yield()方法yield()方法可以对当前线程进行临时暂停(让线程将资源释放出来),供所有线程竞争。3.wait()方法和notify()方法wait()方法类似sleep(),不同的是,wait()会先释放锁住的对象,然后再执行等待的动作。注
25、意,这个函数属于Object类。另外,由于wait()所等待的对象必须先锁住,因此,它只能用在同步化程序段或者同步化方法内,否则,会抛出异常IllegalMonitorStateException。10.4.4 线程常用方法线程常用方法10.4 线程调度线程调度l两个或者多个线程同时想要去获取共享资源的锁,但每个线程都要等其他线程把他们各自的锁给释放才能继续运行,这就是死锁。l比如进程A中包含资源a,进程B中包含资源b,A的下一步需要资源b,B的下一步需要资源a,所以它们就互相等待对方占有的资源释放,所以就产生了一个循环等待获取彼此的锁,这就出现死锁了。10.4.5 线程的死锁线程的死锁10.
26、4 线程调度线程调度能够避免Java线程死锁问题的常用方式有以下几点:1)让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。2)将多个锁组成一组并放到同一个锁下。3)将那些不会阻塞的可获得资源用变量标志出来。10.4.5 线程的死锁线程的死锁10.4 线程调度线程调度1.使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止。2.使用stop()方法强行终止线程。3.使用interrupt()方法中断线程。10.4.6 线程终止线程终止10.5 实训实训10 多线程的练习和应用多线程的练习和应用要求:一个线程继承Thread类,重写
27、run()方法;另一个线程实现Runnable接口,实现run()方法。最后运行start()方法。任务任务1:用继承和实现接口的方式创建两个线程并启动:用继承和实现接口的方式创建两个线程并启动10.5 实训实训10 多线程的练习和应用多线程的练习和应用要求:一个线程在监听按钮事件,另一个线程向屏幕输出数据。任务任务2:创建:创建GUI线程并启动线程并启动10.5 实训实训10 多线程的练习和应用多线程的练习和应用要求:三个线程访问一个共享资源打印机(Printer),每个线程必须打印完其它线程才能使用打印机。任务任务3:同步代码块:同步代码块小结小结多线程的优点:1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快使用多线程的两种实现方式。第一可以继承Thread覆盖它的run()方法;第二种是实现Runnable接口,实现它的run()方法。synchronized 关键字、wait、notify等可以实现线程同步。