《Java高级程序设计》课件-学习情境6 Java多线程与同步机制_第1页
《Java高级程序设计》课件-学习情境6 Java多线程与同步机制_第2页
《Java高级程序设计》课件-学习情境6 Java多线程与同步机制_第3页
《Java高级程序设计》课件-学习情境6 Java多线程与同步机制_第4页
《Java高级程序设计》课件-学习情境6 Java多线程与同步机制_第5页
已阅读5页,还剩98页未读 继续免费阅读

下载本文档

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

文档简介

Java多线程机制一、应用场景二、线程和多线程的概念三、线程的定义、创建和启动四、线程的生命周期、状态及状态之间的转换五、线程的优先级设置本学习情境主要内容3一、应用场景计算机可以同时完成多项任务,称为并发。并发完成的每个任务就是一个独立线程。在网络分布式、高并发应用程序的情况下,Java多线程编程技术在很多开发工作中得到非常广泛的应用。多线程的常见应用场景:(1)后台任务,例如:耗时或大量占用处理器的任务,定时向大量(100w以上)的用户发送邮件;(2)异步处理,例如:发微博、记录日志等;(3)并发运行,例如:视频解码,音频解码,网络解码等等;(4)分布式计算,例如:在两个或多个软件间互相共享信息。二、线程及多线程的概念线程及多线程的概念进程(Process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。线程(Thread):称为轻量级进程(LightweightProcess,LWP),是程序执行流的最小单元。是被系统独立调度和分派的基本单位。多线程(Multithreading):是指从软件或者硬件上实现多个线程并发执行的技术。并发(Concurrency):一个进程可以由多个线程组成,即在一个进程中可以同时运行多个不同的线程,他们分别执行不同的任务。当进程内的多个线程同时运行时,这种运行方式被称为并发运行。三、线程的定义、创建和启动3.1线程的定义线程的定义有两种方式:通过继承Thread类来定义通过实现Runnable接口来定义。定义一个继承Thread类的子类,并重写该类的run()方法。classMyTheadextendsThread{publicvoidrun(){//dosomethinghere}}继承Thread类定义线程类定义Runnable接口的实现类,并重写该接口的run()方法。classMyRunnableimplementsRunnable{publicvoidrun(){//dosomethinghere}}实现Runnable接口线程实现了Runnable接口后还可以继承其他的类。采用继承Thread类,就不能再继承其他类。因为Java只支持单一继承。在实际应用中多数情况都用实现Runnable接口的方式。3.2线程的创建(1)使用线程类的子类创建线程实例对象通过new创建Thread子类的实例,即创建了线程对象。MyThreadoneThread=newMyThread();首先创建Runnable实现类的实例,再以此实例作为Thread的构造函数里的target参数来创建Thread对象,该Thread对象才是真正的线程对象。(2)使用实现Runnable接口的类创建线程实例对象RunnabletwoRunnable=newMyRunnable();ThreadtwoThread=newThread(twoRunnable);3.3线程的启动调用线程的start()方法启动线程。oneThread.start();twoThread.start();创建Runnable实现类的实例并不是真正的线程对象,不能调用start方法启动线程四、线程的生命周期、状态及状态之间的转换线程是一个动态的概念,有创建到终止就是一个生命周期。这是线程的生命周期图在生命周期中有各种各样的状态,这些状态之间也可以相互转换。线程的状态JDK中用Thread.State类定义了线程的6种状态,包含了NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。一个线程可以在给定时间点处于一个状态。同一时刻,线程有且仅有其中的一种状态。RUNNABLE状态包括了操作系统线程状态中的Running和Ready。这些状态是不反映任何操作系统线程状态的虚拟机状态。线程状态间的转换当线程启动后,它不能一直处于运行状态,所以线程要在多条线程之间进行转换,于是线程会在多次运行、阻塞之间进行转换。状态的转换如图1-2所示用new关键字创建一个线程对象后,该线程对象就处于新生状态(NEW)。新生状态(NEW)线程通过调用start方法进入可运行状态(RUNNABLE)。处于可运行状态的线程已经具备了运行条件,但还没有分配到CPU,进入线程就绪队列,等待系统为其分配CPU。可运行状态(RUNNABLE)处于可运行状态的线程,如果获得了CPU的调度,就会从可运行状态变为运行状态,执行run()方法中的任务。处于运行状态的线程最为复杂,它可以变为可运行状态、阻塞状态、等待状态、超时等待状态和终止状态。阻塞状态是线程等待锁的状态。当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchronized(同步),获取不到锁标记,将会立即进入锁池状态线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。阻塞状态等待状态一个线程在等待另一个线程执行动作时处于该状态。调用以下方法线程进入该状态:Object.wait(),Object.join(),LockSupport.park()。超时等待状态线程等待指定时间,进入超时等待状态。调用以下方法进入该状态:Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()、LockSupport.parkUntil()终止状态当线程的run()方法执行完,或者被强制性地终止,线程就进入终止状态。五、线程的优先级设置每个线程执行时都有一定的优先级,优先级高的获得较多的执行机会,优先级低的线程获得较少的执行机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main的优先级是5,由main创建的线程的优先级也是5。线程的优先级线程优先级的设置Thread类提供了setPriority(inti)和getPriority(inti)方法来设置和获得优先级。其中方法中的参数i可以是一个整数,也可以是Thread类的三个静态常量。常量包括:MAX_PRIORITY:其值是10MIN_PRIORITY:其值是1NORM_PRIORITY:其值是5Thread类常用方法start():启动线程并执行相应的run()方法run():子线程要执行的代码放入run()方法中currentThread():静态的,调取当前线程getName():获取此线程的名字setName():设置此线程的名字yield():调用此方法的线程释放当前CPU的执行权join():在A线程中调用B线程的join()方法,表示当执行到此方法,A线程停止执行,指导B线程执行完毕,A线程在接着join()之后的代码执行isAlive():判断线程是否还存活sleep(longlon):显示的让当前线程睡眠lon毫秒小结本学习情境讲述了线程和多线程的概念线程的定义、创建和启动线程的生命周期、状态及状态之间的转换线程的优先级设置线程的常用方法Java多线程的使用实例一、任务描述二、任务分析、设计三、任务实施四、运行结果本学习情境主要内容35一、任务描述使用Java多线程机制编写应用程序任务需求:(1)编写打印杨辉三角形的线程。(2)编写打印斐波那契数列的线程。(3)通过两种方式定义、创建、启动线程。(4)使用调度方法转换线程的状态并显示。(5)设置线程的优先级并调度线程。二、任务分析、设计分析类:本任务中设计三个类:杨辉三角形线程类PascalTriangleThread斐波那契数列线程类FibonacciThread测试主类ThreadUsedTest其类图如图6-1所示。1.分别使用继承Thread类、实现Runable接口两种方式定义两个线程。2.在测试主类的main方法中创建两个线程的对象,然后启动,观察结果。3.通过线程的调度来改变线程的状态,观察结果。4.使用Thread的getPriority()、setPriority()获取、设置线程的优先级。实现思路:三、编码实现3.1定义线程、创建线程、启动线程定义杨辉三角形线程PascalTriangleThreadpublicclassPascalTriangleThreadextendsThread{publicvoidprintPascalTriangle(){//打印杨辉三角形}

publicvoidrun(){

printPascalTriangle(); }}该类继承Thread类,定义打印杨辉三角形的方法,重写run()方法定义斐波那契数列线程FibonacciThreadpublicclassFibonacciThreadimplementsRunnable{ publicvoidprintFibonacci(){ //打印斐波那契数列} @Override publicvoidrun(){

printFibonacci(); }}该类继承实现Runnable接口,定义打印斐波那契数列的方法,重写run()方法创建线程,即创建线程类的对象//创建杨辉三角形的线程对象PascalTriangleThread

ptt=newPascalTriangleThread();FibonacciThreadft=newFibonacciThread();Threadt=newThread(ft);首先创建Runnable实现类的实例,再以此实例作为Thread的构造函数里的target参数来创建Thread对象,该Thread对象才是真正的线程对象。必须清楚:通过Thread的构造函数Thread(Runnabletarget)创建线程对象创建斐波那契数列的线程对象启动线程//启动杨辉三角形线程ptt.start();//启动斐波那契数列线程t.start();从运行结果上可以看到创建的多个线程是交替执行的,这就体现了线程的并发性。3.2通过线程的调度查看线程的状态publicsynchronizedvoidwaitForASecond()throwsInterruptedException{wait(500);//使当前线程等待0.5秒或其他线程调用notify()或notifyAll()方法

}

publicsynchronizedvoidwaitForYears()throwsInterruptedException{wait();//使当前线程永久等待,直到其他线程调用notify()或notifyAll()方法

}

publicsynchronizedvoidnotifyNow()throwsInterruptedException{notify();//唤醒由调用wait()方法进入等待状态的线程

}首先,在FibonacciThread类中增加如下方法,publicvoidrun(){ //TODOAuto-generatedmethodstub try{

printFibonacci();

waitForASecond();//在新线程中运行waitForASecond()方法

waitForYears();//在新线程中运行waitForYears()方法

}catch(InterruptedExceptione){

e.printStackTrace(); }}然后,在FibonacciThread类的run方法中添加对waitForASecond();和waitForYears()方法的调用//1.查看新建线程时的状态FibonacciThreadf1=newFibonacciThread();Threadft1=newThread(f1);System.out.println("新建线程时的状态:"+ft1.getState());通过new创建线程,此时线程的状态为NEW//2.查看启动线程时的状态ft1.start();System.out.println("启动线程时的状态:"+ft1.getState());通过start()启动线程,此时线程的状态为RUNNABLE//3.查看计时等待时的状态Thread.sleep(100);//当前线程休眠0.1秒,使新线程运行waitForASecond()方法System.out.println("计时等待时的状态:"+ft1.getState());当前线程休眠0.1秒,使新线程运行waitForASecond()方法,即运行wait(500);使当前线程等待0.5秒,此时线程的状态为TIMED_WAITING//4.查看wait阻塞时的状态Thread.sleep(1000);//当前线程休眠1秒,使新线程运行waitForYears()方法System.out.println("等待线程时的状态:"+ft1.getState());当前线程休眠1秒,使新线程运行waitForYears()方法,即运行wait()方法,使当前线程永久等待,此时线程的状态为WAITING//5.查看唤醒其它线程时的状态f1.notifyNow();//调用f1的notifyNow()方法System.out.println(“唤醒其它线程时的状态:"+ft1.getState());调用f1的notifyNow()方法,即调用notify()方法,唤醒由调用wait()方法进入等待状态的线程。此时当前线程的状态为RUNNABLE//线程结束后的状态Thread.sleep(1000);//当前线程休眠1秒,使新线程结束System.out.println("线程终止后的状态:"+ft1.getState());线程结束后,此时线程的状态为TERMINATED3.3获取线程的优先级,查看系统默认的线程优先级,以及设置线程优先级PascalTriangleThreadptt2=newPascalTriangleThread();FibonacciThreadft2=newFibonacciThread();Threadftt2=newThread(ft2);首先创建两个线程的对象ftt2.setPriority(10);System.out.println("斐波那契数列线程的优先级:"+ftt2.getPriority());ptt2.setPriority(1);System.out.println("杨辉三角形线程的优先级:"+ptt2.getPriority());再通过getPriority()方法获取优先级通过setPriority()方法设置优先级运行结果小结本学习情境通过一个实例讲述了线程的声明、创建、启动线程的状态线程的优先级多线程的同步机制一、应用场景二、多线程同步的概念三、Java多线程的同步机制四、常用的三种同步方法本学习情境主要内容61一、应用场景当多个线程要同时访问(如数据的增删改查)一个共享变量、同一个对象或同一个对象的方法时,这些线程中既有读又有写操作,将会导致数据不准确,相互之间产生冲突。多线程同步就是要解决这个问题。二、多线程同步的概念多线程同步,就是多线程间在一些关键点上可能需要互相等待与互通消息,这种相互制约的等待与互通信息称为多线程同步。在多线程编程里面,某一资源同时只允许一个访问者对其进行访问,具有排他性,即互斥。在互斥的基础上,通过同步机制实现访问者对资源的有序访问,确保资源在任何时刻最多只有一个线程访问,保证数据的完整性。多线程同步是为了确保线程安全,所谓线程安全指的是多个线程对同一资源进行访问时,有可能产生数据不一致问题,如果多线程程序运行结果和单线程运行的结果是一样的,且相关变量的值与预期值一样,则是线程安全的。三、多线程的同步机制Java提供的多线程同步机制,是使用一些关键字、接口和类以及常用调度方法来实现多线程的同步。多线程的同步方法大体分为以下几种:1.使用同步关键字(synchronized)2.使用重入锁类(ReentrantLock)3.使用特殊域变量关键字(volatile)4.使用局部变量(ThreadLocal)5.使用原子变量(AtomicInteger)6.使用阻塞队列三、常用的三种同步方法4.1synchronized关键字synchronized关键字,是比较常用的用来控制线程同步的关键字,保证在多线程环境下,同步对象不被多个线程同时执行,以确保数据的完整性。synchronized提供了一种独占的加锁方式,一般在“线程安全的单例”中普遍使用。该关键字一般用在代码块和方法上。synchronized关键字修饰的代码块称为同步块synchronized(a1){//操作}即用synchronized关键字修饰的语句块。

被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。(1)同步代码块(2)同步实例方法即用synchronized关键字修饰类的方法。publicsynchronizedvoidincrease(){

i++;}由于Java的每个对象都有一个内置锁,当用此关键字修饰方法时,

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。(3)同步静态方法用synchronized修饰静态方法。publicstaticsynchronizedvoidincrease(){

i++;}由于静态成员不专属于任何一个实例对象,是类成员,因此如果调用该静态方法,将会锁住整个类。(1)synchronized锁的是括号里的对象,而不是代码。当某一线程使用synchronized锁住一个对象之后,其他线程就必须等,直到该线程释放锁对象。(2)对于非静态的synchronized方法,锁的是对象本身也就是this。(3)同步是一种高开销的操作,因此应该尽量减少同步的内容。减小锁粒度也是一种削弱多线程锁竞争的一种有效手段。需要注意几点:4.2Volatile关键字Java语言提供了一种稍弱的同步机制,即volatile关键字。volatile的字面意思是“易变的、不稳定的”。作为Java中的关键字,它表示被修饰的变量的值容易变化(被其他线程修改),因此不稳定。publicclassBank{privatevolatileintcount=0;//账户余额

//存钱

publicvoidaddMoney(intmoney){}//取钱

publicvoidsubMoney(intmoney){}//查询

publicvoidlookMoney(){}}使用volatile关键字修饰count变量在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。(1)volatile关键字为域变量的访问提供了一种免锁机制;(2)使用volatile修饰域变量相当于告诉虚拟机该域变量可能会被其他线程更新;(3)因此每次使用该域变量就要重新计算,而不是使用寄存器中的值;(4)volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。4.3Lock接口在JDK5中,Java引入了一个概念Lock,也就是锁。功能与synchronized类似。Lock接口中的方法lock()是用来获取锁的。unLock()方法是用来释放锁的。如果锁已被其他线程获取,则线程等待。在java.util.concurrent.locks包下有Lock、ReadWriteLock接口以及实现这两个接口的ReentrantLock类、ReentrantReadWriteLock类。Lock接是接口,通常使用其实现类,如,ReentrantLock类、ReentrantReadWriteLock类。使用方法首先使用Lock的实现类ReentrantLock创建一个锁对象,然后在方法中调用lock()方法获取锁,执行操作,最后调用unlock()方法释放锁ReentrantLock称为重入锁。是唯一实现了Lock接口的类,并且提供了更多的方法。它比synchronized拥有更加强大的功能,它可以中断、可定时。在高并发的情况下,它比synchronized有明显的性能优势。Reentrantlock提供了非常丰富的锁控制功能,灵活应用这些控制方法,可以提高应用程序的性能。重入锁算是JDK中提供的高级开发工具。Lock和synchronized有以下几点不同:(1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;(2)synchronized在发生异常时,会自动释放锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;(3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;(4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。(5)Lock可以提高多个线程进行读操作的效率。小结本学习情境讲述了多线程同步的概念Java多线程同步机制三种主要的同步方法synchronized

volatileLock接口和ReentrantLock类多线程同步机制的使用实例一、任务描述二、务分析、设计三、泛型类的定义和使用四、泛型方法的定义和使用五、泛型接口的定义和使用本学习情境主要内容85一、任务描述使用Java多线程的同步机制编写应用程序任务需求:模拟火车站售票大厅进行火车票售票过程有四个窗口,每个窗口是一个线程不能出现多个窗口出售同一张票二、任务分析、设计分析:(1)火车票使用同一个静态值。(2)为保证不会出现卖出同一张票,使用Java多线程同步机制。1.设计一个票池,作为共享资源,每张票包含座位号。2.创建售票窗口类TicketWindow,作为线程类,在run方法里面执行售票操作,售票要使用同步机制:即有一个窗口卖某张票时,其他窗口不能卖这张票。3.创建售票大厅类TestTicketLobby,在ma

温馨提示

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

评论

0/150

提交评论