版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第八章多线程程序设计8.1多线程编程基础8.2线程间的数据共享8.3多线程同步8.4线程通信8.5线程的生命周期8.6线程的优先级8.1多线程编程基础线程基本概念Thread类Runnable接口Thread类常用方法8.1.1线程的概念多重编程:一台计算机一次运行多个应用程序(如同时运行Excel和Word)在现代计算系统中,通常有多个要执行的并发应用程序进程。在计算机系统中,有多个等待执行的进程,即它们正在等待将CPU分配给它们并开始执行的时间,这些过程也称为作业。现在,主内存太小,无法容纳所有这些进程或作业。因此,这些过程最初保留在称为作业池的区域中。该作业池由所有等待分配主内存和CPU的进程组成。CPU从所有这些等待的作业中选择一个作业,将其从作业池移至主内存并开始执行。处理器执行一项作业,直到它被某个外部因素中断或执行I/O任务为止。多处理:一台计算机一次使用多个CPU系统在单个计算机系统中支持一个以上处理器的能力。现在,由于有多个处理器可用,可以一次执行多个进程。多重处理和多重编程的区别,多重处理基本上是在多个处理器上同时执行多个进程,而多重编程是将多个程序保留在主存储器中,并仅使用单个CPU并行执行。多重处理是通过并行处理发生的,而多重编程是通过从一个过程切换到另一个过程而发生的(现象称为上下文切换)。8.1.1线程的概念多任务处理:任务共享一个公共资源(如1个CPU)多任务处理是指一次执行多个任务(例如,进程,程序,线程等)。在现代操作系统中,能够播放MP3音乐,在MicrosoftWord中编辑文档,同时浏览GoogleChrome,这是通过多任务处理来完成的。多任务处理是多程序设计的逻辑扩展。多任务与多编程不同的主要方式是,多编程仅基于上下文切换的概念进行工作,而多任务则基于时间共享以及上下文切换的概念。多线程:多任务的扩展线程是CPU利用率的基本单位。多线程是一种执行模型,它允许单个进程具有在该进程的“上下文”中并发运行的多个代码段(即线程)。例如:媒体播放器,其中一个线程用于打开媒体播放器,一个线程用于播放特定歌曲,另一个线程用于向播放列表添加新歌曲。多线程是一种过程,一次可以管理一个以上的用户,并可以管理同一用户的多个请求,而不必拥有多个程序副本。进程介绍进程(Process):是正在运行程序的实例,是操作系统资源分配的基本单元。每个进程都有自己独立的内存空间和系统资源。简单理解:程序的执行过程8.1.1线程的概念1.独立性:每一个进程都有自己的空间,在没有经过进程本身
允许的情况下,一个进程不可以直接访问其它的的进程空间动态性:进程是动态产生,动态消亡的并发性:任何进程都可以同其它进程一起并发执行并行和并发并行:在同一时刻,有多个指令在多个CPU上【同时】执行并发:在同一时刻,有多个指令在单个CPU上【交替】执行8.1.1线程的概念多进程同时执行8.1.1线程的概念线程介绍线程(Thread):是进程的执行单元,是cpu调度的最小单位。一个进程可以包含多个线程,他们共享进程的内存空间和系统资源独立性:每一个进程都有自己的空间,在没有经过进程本身允许的情况下,一个进程不可以直接访问其它的的进程空间8.1.1线程的概念多线程的意义随着处理器上的核心数量越来越多,现在大多数计算机都比以往更加擅长并行计算一个线程,在一个时刻,只能运行在一个处理器核心上8.1.1线程的概念4核8线程8核16线程16核32线程32核64线程2核4线程8.1.1线程的概念4核8线程8核16线程16核32线程32核64线程2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念2核4线程8.1.1线程的概念多线程的意义提高执行效率同时处理多个任务8.1.1线程的概念8.1.1线程的概念多任务环境中的应用程序进程中的线程进程是可以并行执行的代码片段,线程之间可以共享数据8.1.1线程的概念进程一个独立程序的每一次运行称为一个进程,例如用字处理软件编辑文稿时,同时打开播放程序听音乐,这两个独立的程序在同时运行,称为两个进程。设置一个进程要占用相当一部分处理器时间和内存资源大多数操作系统不允许进程访问其他进程的内存空间,进程间的通信很不方便,编程模型比较复杂。8.1.1线程的概念线程一个程序中多段代码同时并发执行,称为多线程通过多线程,一个进程表面上看同时可以执行一个以上的任务——并发创建线程比创建进程开销要小得多,线程之间的协作和数据交换也比较容易Java是第一个支持内置线程操作的主流编程语言多数程序设计语言支持多线程要借助于操作系统“原语(primitives)”8.1.1线程的概念进程vs线程多个进程的内部数据和状态都是完全独立的,多线程可以共享进程所拥有的内存空间中的同一区域以及系统资源,有可能相互影响。线程本身运行所需的环境比较简单,通常只包括寄存器及堆栈,切换速度比进程快得多。进程是操作系统分配内存及其他系统资源的基本单元,线程是操作系统分配CPU的基本单元。在开发中应用多线程技术的主要目的是“最大限度地利用CPU资源”。比如当某一线程的处理不需要占用CPU时,就会让给其他线程使用。多任务处理是指一次执行多个任务(例如,进程,程序,线程等)。8.1.1线程的概念Java线程的6种状态新建状态运行状态等待状态阻塞状态终止状态计时等待状态8.1.2Thread类Thread类在Java程序中创建多线程的方法之一是继承Thread类封装了Java程序中一个线程对象需要拥有的属性和方法从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread类的run方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程将自动进入run方法。原线程将同时继续往下执行Thread类直接继承了Object类,并实现了Runnable接口。它位于java.lang包中,因而程序开头不用import任何包就可直接使用。继承Thread类实现Runnable接口实现Callable接口推荐这两种方式,扩展性更好线程任务无返回值线程任务有返回值Java开启线程的方式8.1.2Thread类8.1.2Thread类在新线程中完成计算某个整数的阶乘classFactorialThreadextendsThread{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){inti=num;intresult=1;System.out.println("newthreadstarted");while(i>0){ result=result*i; i=i-1;}System.out.println("Thefactorialof"+num+"is"+result);System.out.println("newthreadends");}}运行结果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadendspublicclassMultiTreadEx1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadthread=newFactorialThread(10);thread.start();System.out.println("mainthreadends");}}8.1.2Thread类结果说明main线程已经执行完后,新线程才执行完main方法调用thread.start()方法启动新线程后并不等待其run方法返回就继续运行,thread.run()方法在一边独自运行,不影响原来的main方法的运行源程序修改如果启动新线程后希望主线程多持续一会再结束,可在start语句后加上让当前线程(这里当然是main)休息100毫秒的语句:try{Thread.sleep(100);}catch(Exceptione){};§7.1.2Thread类修改后运行结果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends运行结果说明新线程结束后main线程才结束publicclassMultiTreadEx1{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadthread=newFactorialThread(10);
thread.start();try{Thread.sleep(1);}catch(Exceptione){};System.out.println("mainthreadends");}}8.1.2Thread类实现多线程运行的基本方法-1可以直接从Thread类派生,重写其run方法:
调用Thread类的start()方法启动运行示例:ThreadRun.java注意主线程有一个“main”的名字,其它的线程名格式为“Thread-序号”。8.1.2Thread类创建3个新线程,每个线程睡眠一段时间(0~6秒),然后结束publicclassMultiTreadEx2{publicstaticvoidmain(String[]args){//创建并命名每个线程
TestThreadthread1=newTestThread("thread1");TestThreadthread2=newTestThread("thread2");TestThreadthread3=newTestThread("thread3");System.out.println("Startingthreads");thread1.start();//启动线程1thread2.start();//启动线程2thread3.start();//启动线程3System.out.println("Threadsstarted,mainends\n");}}classTestThreadextendsThread{privateintsleepTime;publicTestThread(Stringname){super(name);sleepTime=(int)(Math.random()*6000);}publicvoidrun(){try{System.out.println(getName()+"goingtosleepfor"+sleepTime);Thread.sleep(sleepTime);//线程休眠
}catch(InterruptedExceptionexception){;}System.out.println(getName()+"finished");}}8.1.2Thread类运行结果StartingthreadsThreadsstarted,mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished说明由于线程3休眠时间最长,所以最后结束,线程2休眠时间最短,所以最先结束每次运行,都会产生不同的随机休眠时间,所以结果都不相同8.1.3Runnable接口实现多线程运行的基本方法-2将代码封装在Runable接口的run()方法内:8.1.3Runnable接口实现多线程运行的基本方法-2将代码封装在Runable接口的run()方法内:创建一个Runable对象:将Runable对象传给Thread对象:启动多线程运行:示例:RunableThreadTest.java主线程运行结束完毕,辅助线程仍在运行。只有两个线程都运行完毕,进程才算终止。8.1.3Runnable接口Runnable接口Java多线程机制的一个重要部分,实际上它只有一个run()方法Thread类实现了Runnable接口,它更适合于多个线程处理同一资源实现Runnable接口的类的对象可以用来创建线程,这时start方法启动此线程就会在此线程上运行run()方法在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线程8.1.3Runnable接口使用Runnable接口实现例MultiTreadEx1功能classFactorialThreadimplementsRunnable{privateintnum;publicFactorialThread(intnum){this.num=num;}publicvoidrun(){inti=num;intresult=1;while(i>0){result=result*i;i=i-1;}System.out.println("Thefactorialof"+num+"is"+result);System.out.println("newthreadends");}}8.1.3Runnable接口publicclassMultiTreadEx3{publicstaticvoidmain(String[]args){System.out.println("mainthreadstarts");FactorialThreadt=newFactorialThread(10);
newThread(t).start();System.out.println("newthreadstarted,mainthreadends");}}8.1.3Runnable接口使用Runnable接口实现例MultiTreadEx2功能publicclassMultiTreadEx4{publicstaticvoidmain(String[]args){TestThreadthread1=newTestThread();TestThreadthread2=newTestThread();TestThreadthread3=newTestThread();System.out.println("Startingthreads");
newThread(thread1,"Thread1").start();newThread(thread2,"Thread2").start();newThread(thread3,"Thread3").start();
System.out.println("Threadsstarted,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){;}System.out.println(Thread.currentThread().getName()+"finished");}}63名称说明publicThread()构造一个新的线程对象,默认名为Thread-n,n是从0开始递增的整数publicThread(Runnabletarget)构造一个新的线程对象,以一个实现Runnable接口的类的对象为参数。默认名为Thread-n,n是从0开始递增的整数publicThread(Stringname)构造一个新的线程对象,并同时指定线程名publicThread(Runnabletask,Stringname)构造一个新的线程对象,以一个实现Runnable接口的类的对象为参数,并同时指定线程名publicstaticThreadcurrentThread()返回当前正在运行的线程对象publicstaticvoidyield()使当前线程对象暂停,允许别的线程开始运行publicstaticvoidsleep(longmillis)使当前线程暂停运行指定毫秒数,但此线程并不失去已获得的锁旗标。8.1.4Thread类常用方法64publicvoidstart()启动线程,JVM将调用此线程的run方法,结果是将同时运行两个线程,当前线程和执行run方法的线程publicvoidrun()Thread的子类应该重写此方法,内容应为该线程应执行的任务。publicfinalvoidstop()停止线程运行,释放该线程占用的对象锁旗标。publicvoidinterrupt()中断此线程publicfinalvoidjoin()在当前线程中加入调用join方法的线程A,直到线程A死亡才能继续执行当前线程publicfinalvoidjoin(longmillis)在当前线程中加入调用join方法的线程A,直到到达参数指定毫秒数或线程A死亡才能继续执行当前线程8.1.4Thread类常用方法65publicfinalvoidsetPriority(int
newPriority)设置线程优先级publicfinalvoidsetDaemon(Booleanon)设置是否为后台线程,如果当前运行线程均为后台线程则JVM停止运行。这个方法必须在start()方法前使用publicvoidsetName(Stringname)更该本线程的名称为指定参数publicfinalboolean
isAlive()测试线程是否处于活动状态,如果线程被启动并且没有死亡则返回true8.1.4Thread类常用方法8.1.4Thread类常用方法Thread类Run()方法的奥秘Thread类实现了Runnable接口,所以,Thread类也有Run()方法。Target字段在调用Thread类的构造函数时被设置。当提供了Runnable对象时,target引用它。当没有提供Runnable对象时,target为空。所以,如果我们从Thread类中派生子类,要重写Run方法,在其中写上“干活”的代码8.1.4Thread类常用方法挑战以下代码将运行哪个run()方法?
示例:RunWhatMethod.java
8.1.4Thread类常用方法暂停线程的执行可以使用Thread.sleep()方法,让线程休眠指定的时间,从而拖慢线程的运行。注意:要捕获InterruptedException异常Demo:
ThreadSleep.java8.1.4Thread类常用方法另一种暂停线程的方法使用TimeUnit枚举类型的相应方法,其好处是能直接指定各种不同的时间单位:纳秒、微秒、毫秒、秒、分钟、小时、天Demo:
ThreadSleep.java8.1.4Thread类常用方法中止一个线程当线程方法执行完毕时,线程自然中止当线程中发生未捕获的异常时,操作系统往往会结束整个进程其它线程请求中断当前线程的工作,当前线程响应这个请求而提前结束工作。8.1.4Thread类常用方法向线程发出中断请求外界通过调用线程的interrupt()方法向线程发出中断工作的请求,这时,线程对象内部的一个“中断状态”标记被设置。线程对象定期检查这一标记,以合适的方式中断已经运行的工作8.1.4Thread类常用方法向线程发出中断请求如果线程自己要调用sleep()方法,由于此方法在运行时会清除掉“中断状态标记”,并激发一个InterruptedException,所以,应该采用以下代码模板:中断一个线程执行的示例Demo:PrimeGenertor此示例不断计算一个数是否是素数,XX秒之后自动中断。8.1.4Thread类常用方法Java线程优先级JavathreadpriorityPriorityinrange1-10,默认值为5。线程创建时,子线程继承父线程的优先级线程创建完毕后,可以通过调用setPeriority方法改变优先级。操作系统线程调度,根据优先级进行非抢占调度:英国的绅士风度,java中使用Thread类的yield()方法实现抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。8.1.3Runnable接口实现多线程运行的基本方法-3实现Callable接口//1.编写一个类实现Callable接口classMyCallableimplementsCallable<Integer>{//2.重写call方法
@OverridepublicInteger
call()throwsException{//3.将线程任务代码写在call方法中
intsum=0;for(inti=1;i<=100;i++){sum+=i;System.out.println("sum="+sum);}returnsum;}}8.1.3Runnable接口实现多线程运行的基本方法-3实现Callable接口publicclassThreadDemo4{publicstaticvoidmain(String[]args)throwsException{
//4:
创建线程任务资源对象
MyCallablemc=newMyCallable();
//5:创建线程任务对象,封装线程资源
FutureTask<Integer>task1=newFutureTask<>(mc);FutureTask<Integer>task2=newFutureTask<>(mc);
//6:创建线程对象,传入线程任务
Threadt1=newThread(task1);Threadt2=newThread(task2);
//7:使用线程对象调用start开启线程
t1.start();t2.start();Integerresult1=task1.get();Integerresult2=task2.get();System.out.println("task1获取到的结果为:"+result1);System.out.println("task2获取到的结果为:"+result2);}}8.2线程间的数据共享代码共享多个线程的执行代码来自同一个类的run方法时,即称它们共享相同的代码数据共享当共享访问相同的对象时,即它们共享相同的数据使用Runnable接口可以轻松实现多个线程共享相同数据,只要用同一个实现了Runnable接口的实例作为参数创建多个线程就可以了8.2线程间的数据共享修改例MultiTreadEx4,只用一个Runnable类型的对象为参数创建3个新线程。publicclassMultiTreadEx5{publicstaticvoidmain(String[]args){TestThreadthreadobj=newTestThread();System.out.println("Startingthreads");
newThread(threadobj,"Thread1").start();newThread(threadobj,"Thread2").start();newThread(threadobj,"Thread3").start();
System.out.println("Threadsstarted,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){}; System.out.println(Thread.currentThread().getName()+"finished");}}8.2线程间的数据共享运行结果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted,mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished说明因为是用一个Runnable类型对象创建的3个新线程,这三个线程就共享了这个对象的私有成员sleepTime,在本次运行中,三个线程都休眠了966毫秒8.2线程间的数据共享独立的同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作例如生产/消费问题:生产线程产生数据流,然后这些数据流再被消费线程消费假设一个Java应用程序,其中有一个线程负责往文件写数据,另一个线程从同一个文件中往出都数据,因为涉及到同一个资源,这里是同一个文件,这两个线程必须保证某种方式的同步8.2线程间的数据共享用三个线程模拟三个售票口,总共出售200张票用3个线程模仿3个售票口的售票行为这3个线程应该共享200张票的数据publicclassMultiTreadEx6{publicstaticvoidmain(String[]args){ SellTicketst=newSellTickets();newThread(t).start(); newThread(t).start(); newThread(t).start();}}classSellTicketsimplementsRunnable{privateinttickets=200;publicvoidrun(){
while(tickets>0){ System.out.println(Thread.currentThread().getName()+ "issellingticket"+tickets--); }}}8.2线程间的数据共享运行结果选最后几行如下Thread-2issellingticket6Thread-1issellingticket5Thread-0issellingticket4Thread-2issellingticket3Thread-1issellingticket2Thread-0issellingticket1说明在这个例子中,创建了3个线程,每个线程调用的是同一个SellTickets对象中的run()方法,访问的是同一个对象中的变量(tickets)如果是通过创建Thread类的子类来模拟售票过程,再创建3个新线程,则每个线程都会有各自的方法和变量,虽然方法是相同的,但变量却是各有200张票,因而结果将会是各卖出200张票,和原意就不符了8.3多线程同步多线程程序的不确定性如果两个线程同时运行:TwoThreadTest.java注意:只有调用Thread类的Start方法,才能真正地在一个独立的线程中执行代码,直接调用Thread类的run方法,并不能启动一个新的线程,代码是在调用者线程中执行的。8.3多线程同步同步方法与同步代码块为什么要同步线程?在多线程情况下,以上代码有何问题?8.3多线程同步引发问题的根源解决方法:给要访问数据的线程添加一个规定:一次只允许一个线程访问数据。只有“当前正在访问数据”的线程结束访问之后,其他线程才允许访问这个数据。线程安全和同步安全问题出现的条件
是多线程环境
有共享数据
有多条语句操作共享数据同步技术:
将多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程可以执行同步代码块同步方法8.3多线程同步8.3多线程同步编写线程安全的代码——同步代码块亦可使用同步代码块达到相同的目的:TestThread1.javaTestThread2.java上述代码中的object代表同步对象,通常我们会将要访问同步对象的代码放入到此同步块中(这并非是必须遵守的规则,完全可以放置任意的代码)。JVM会保证只有一个线程执行代码块中的这些代码。8.3多线程同步编写线程安全的代码——同步方法添加了synchronized关键字的方法称为“同步方法”。每次只允许一个线程执行同步方法。其余的线程阻塞,直到此方法执行完毕当方法执行完毕,JVM调度下一个最高优先级的线程运行。8.3多线程同步设计同步方法在实际开发中,通常在内部放置一个专用于同步的对象,在同步方法内部锁定它:Demo:SynchronizedMethodTest.java8.3多线程同步同步对象引发的问题剖析示例:UseSynchronized.java实验:锁定同一对象实现实例方法的同步锁定不同对象实现实例方法的同步如何实现实例方法与静态方法的同步8.3多线程同步同步方法小结-1当synchronized方法执行完毕或发生异常时,会自动释放锁。被synchronized保护的数据应该是私有的。多个线程对同一个对象的成员变量进行操作时,他们对该成员变量的操作是互相影响的。方法内部所定义的局部变量,每个执行它的线程都会有一个拷贝,访问它是安全的。线程访问synchronized静态方法时,会锁定其class对象,这将导致所有调用此静态方法的线程“串行”执行。8.3多线程同步同步方法小结-2当synchronized方法是一种粗粒度的并发控制手段,某一时刻只能有一个线程执行该方法。synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内synchronized块之外的代码是可以被多个线程同时访问到。注意:不恰当的锁定,可能会带来严重的性能问题。8.3多线程同步让多个线程协同工作……线程同步等待另一个线程的结束A线程正在运行,希望插入一个B线程,要求B先执行完毕,A才继续运行使用Join方法Join的含义:将某一线程加入成为另一个线程的流程之一。8.3多线程同步让多个线程协同工作……线程同步等待另一个线程的结束A线程正在运行,希望插入一个B线程,要求B先执行完毕,A才继续运行使用Join方法Join的含义:将某一线程加入成为另一个线程的流程之一。示例:JoinTest.java。不使用join方法,将导致两个线程的交错执行。8.3多线程同步有时线程之间彼此不独立、需要同步线程间的互斥同时运行的几个线程需要共享一个(些)数据一个线程对共享的数据进行操作时,不允许其他线程打断它,否则会破坏数据的完整性。即被多个线程共享的数据,在某一时刻只允许一个线程对其进行操作“生产者/消费者”问题生产者产生数据,消费者消费数据,具体来说,假设有一个Java应用程序,其中有一个线程负责往数据区写数据,另一个线程从同一数据区中读数据,两个线程可以并行执行(类似于流水线上的两道工序)如果数据区已满,,生产者要等消费者取走一些数据后才能再放;而当数据区没有数据时,消费者要等生产者放入一些数据后再取8.3多线程同步用两个线程模拟存票、售票过程假定开始售票处并没有票,一个线程往里存票,另外一个线程则往出卖票我们新建一个票类对象,让存票和售票线程都访问它。本例采用两个线程共享同一个数据对象来实现对同一份数据的操作publicclassMultiTreadEx7{publicstaticvoidmain(String[]args){ Ticketst=newTickets(10); newConsumer(t).start();
newProducer(t).start();
}}8.3多线程同步classTickets{intnumber=0;//票号
intsize;//总票数
booleanavailable=false;//表示目前是否有票可售
publicTickets(intsize)//构造函数,传入总票数参数
{ this.size=size;} }8.3多线程同步classProducerextendsThread{Ticketst=null;publicProducer(Ticketst){ this.t=t;}publicvoidrun(){ while(t.number<t.size) { System.out.println("Producerputsticket"+(++t.number));t.available=true; } }}8.3多线程同步classConsumerextendsThread//售票线程{Ticketst=null;inti=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; } }}8.3多线程同步运行结果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10Consumerbuysticket9Consumerbuysticket10.通过让两个线程操纵同一个票类对象,实现了数据共享的目的线程通信介绍确保线程能够按照预定的顺序执行,并且能够安全地访问共享资源8.4线程通信线程通信介绍8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权嗝~不是...???!!你全吃了啊?8.4线程通信线程通信介绍8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信线程通信介绍抢到执行权8.4线程通信确保线程能够按照预定的顺序执行
并且能够安全地访问共享资源使多条线程更好的进行协同工作等待唤醒机制成员方法说明voidwait()使当前线程等待voidnotify();随机唤醒单个等待的线程这些方法需要使用锁对象调用注意事项8.4线程通信等待唤醒机制成员方法说明voidwait()使当前线程等待voidnotify();随机唤醒单个等待的线程voidnotifyAll();唤醒所有等待的线程这些方法需要使用锁对象调用注意事项8.4线程通信等待唤醒机制成员方法说明voidwait()使当前线程等待voidnotify();随机唤醒单个等待的线程voidnotifyAll();唤醒所有等待的线程这些方法需要使用锁对象调用注意事项wait()
方法在等待的时候会释放锁对象8.4线程通信等待唤醒机制成员方法说明voidawait()指定线程等待voidsignal();指定唤醒单个等待的线程使用ReentrantLock实现同步,并获取Condition对象8.4线程通信Condition是一个接口,它提供了线程之间的协调机制,可以将它看作是一个更加灵活、更加强大的wait()和notify()机制,通常与Lock接口(比如ReentrantLock)一起使用。8.4线程通信importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.ReentrantLock;ClassA{privatefinalReentrantLock
lock=newReentrantLock();privatefinalConditioncondition=lock.newCondition();
Publicvoidm(){
lock.lock(); try{//保证线程安全的代码; } catch(InterruptedExceptione){ e.printStackTrace();}finally{
lock.unlock();//如果同步代码有异常,要将unlock()写入finally语句块。}}}8.3线程通信需求:创建两个线程,一个线程负责打印数组中的每个元素,另一个线程负责打印字母;两个线程交替打印。数组中元素总数为5。8.3线程通信publicclassAlternatePrint1{privatestaticfinalObjectlock=newObject();privatestaticvolatileintindex=0;privatestaticvolatilecharletter='A';publicstaticvoidmain(String[]args){RunnableprintArrayRunnable=newPrintArrayRunnable();RunnableprintLetterRunnable=newPrintLetterRunnable();Threadthread1=newThread(printNumberRunnable);Threadthread2=newThread(printLetterRunnable);thread1.start();thread2.start();}8.3线程通信privatestaticclassPrintArrayRunnableimplementsRunnable{privateStringarr[]={"java","c语言","c++","python","go"};@Overridepublicvoidrun(){synchronized(lock){while(index<arr.length){System.out.print(arr[index]+"");index++;lock.notify();//唤醒等待的线程
try{lock.wait();//等待其他线程打印
}catch(InterruptedExceptione){e.printStackTrace();}}}}}8.3线程通信privatestaticclassPrintLetterRunnableimplementsRunnable{@Overridepublicvoidrun(){synchronized(lock){while(letter<='D'){System.out.print(letter+"");letter++;lock.notify();//唤醒等待的线程
try{lock.wait();//等待其他线程打印
}catch(InterruptedExceptione){e.printStackTrace();}}}}}}生产者消费者模式生产者消费者模式是一个十分经典的多线程协作的模式主包含了两类线程:
生产者线程,用于生产数据
消费者线程,用于消费数据8.4线程通信负责消费生产者消费者模式WareHouse负责生产Producerboolenmark=false;判断包子的状态false:生产true:等待判断包子的状态false:等待true:开吃Consumer8.4线程通信生产者消费者模式WareHouse负责生产负责消费ProducerConsumerboolenmark=false;判断包子的状态false:生产
改变mark状态
唤醒消费者线程true:等待判断包子的状态false:等待true:开吃8.4线程通信生产者消费者模式WareHouse负责生产负责消费Producerboolenmark=false;判断包子的状态false:生产
改变mark状态
唤醒消费者线程true:等待判断包子的状态false:等待true:开吃
改变mark状态
唤醒生产者线程Consumer8.4线程通信生产者消费者模式生产者消费者模式是一个十分经典的多线程协作的模式主包含了两类线程:
生产者线程,用于生产数据
消费者线程,用于消费数据为了解耦生产者和消费者的关系,通常会采用共享的数据区域(缓冲区),就像是一个仓库生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为8.4线程通信8.5线程生命周期线程的生命周期线程从产生到消亡的过程一个线程在任何时刻都处于某种线程状态(threadstate)8.5线程生命周期线程生命周期线程的启动后的各种状态线程被创建并启动以后,它并不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态
状态具体含义NEW(新建)创建线程对象RUNNABLE(就绪)start方法被调用,但是还没有抢到CPU执行权BLOCKED(阻塞)线程开始运行,但是没有获取到锁对象WAITING(等待)wait方法TIMED_WAITING(计时等待)sleep方法TERMINATED(结束状态)代码全部运行完毕8.5线程生命周期run();结束运行死亡阻塞无限等待计时等待就绪新建线程生命周期创建线程对象有执行资格没有执行权没有执行资格没有执行权没有执行资格没有执行权线程死亡变成垃圾没有执行资格没有执行权有执行资格有执行权start();抢到CPU执行权其他线程抢走CPU执行权sleep(计时);到时间了wait();notify();无法获取锁获取到锁8.5线程生命周期死锁线程在运行过程中,其中某个步骤往往需要满足一些条件才能继续进行下去,如果这个条件不能满足,线程将在这个步骤上出现阻塞线程A可能会陷于对线程B的等待,而线程B同样陷于对线程C的等待,依次类推,整个等待链最后又可能回到线程A。如此一来便陷入一个彼此等待的轮回中,任何线程都动弹不得,此即所谓死锁(deadlock)对于死锁问题,关键不在于出现问题后调试,而是在于预防8.5线程生命周期设想一个游戏,规则为3个人站在三角形的三个顶点的位置上,三个边上放着三个球,如图所示。每个人都必须先拿到自己左手边的球,才能再拿到右手边的球,两手都有球之后,才能够把两个球都放下Player_0Player_1Player_20218.5线程生命周期结束线程的生命正常运行结束使用退出标志退出线程interrupt方法结束线程;isInterrupted()stop()方法已弃用;(使用Thread.stop()可以立即终止线程,但这是一种非常危险的做法,因为它不保证资源的正常释放,可能会导致程序状态不一致,因此它已经在Java中被弃用了。)8.5线程生命周期正常运行结束publicclassMyRunnableimplementsRunnable{publicvoidrun(){//模拟任务代码
System.out.println("任务开始执行");try{Thread.sleep(2000);//模拟耗时操作
}catch(InterruptedExceptione){Thread.currentThread().interrupt();//重新设置中断状态
}System.out.println("任务执行结束");}}publicclassThreadEndDemo{publicstaticvoidmain(String[]args){Threadt=newThread(newMyRunnable());t.start();}}8.5线程生命周期使用退出标志退出线程publicclassFlagRunnableimplementsRunnable{privatevolatilebooleanexit=false;publicvoidrun(){while(!exit){//...执行任务
}System.out.println("退出标志被设置,线程结束运行");}publicvoidsetExit(booleanexit){this.exit=exit;}}publicclassFlagThreadDemo{publicstaticvoidmain(String[]args){FlagRunnablerunnable=newFlagRunnable();Threadt=newThread(runnable);t.start();//在恰当的时刻设置退出标志
runnable.setExit(true);}}8.5线程生命周期使用Interrupt方法正确终止线程publicclassInterruptedRunnableimplementsRunnable{publicvoidrun(){try{while(!Terrupted()){//...执行复杂任务
//模拟长时间运行操作
Thread.sleep(5000);}}catch(InterruptedExceptione){System.out.println("线程被中断");//可以在这里做一些资源清理操作
}finally{System.out.println("线程优雅地结束运行");}}}publicclassInterruptDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt=newThread(newInterruptedRunnable());t.start();//给线程一些时间执行任务
Thread.sleep(2000);//请求中断线程
errupt();}}8.6线程的优先级线程调度在单CPU的系统中,多个线程需要共享CPU,在任何时间点上实际只能有一个线程在运行控制多个线程在同一个CPU上以某种顺序运行称为线程调度Java虚拟机支持一种非常简单的、确定的调度算法,叫做固定优先级算法。这个算法基于线程的优先级对其进行调度8.6线程的优先级线程的优先级每个Java线程都有一个优先级,其范围都在1和10之间。默认情况下,每个线程的优先级都设置为5在线程A运行过程中创建的新的线程对象B,初始状态具有和线程A相同的优先级如果A是个后台线程,则B也是个后台线程可在线程创建之后的任何时候,通过setPriority(intpriority)方法改变其原来的优先级8.6线
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 氯化苯装置操作工安全素养能力考核试卷含答案
- 母婴护理员岗中风险识别考核试卷含答案
- 塔盘制炼工岗前进阶考核试卷含答案
- 眼镜定配工岗前技能安全考核试卷含答案
- 造林更新工岗中技术实操考核试卷含答案
- 2026年秋季为什么流感每年都会来
- 2025年铜仁地区沿河土家族自治县三下数学期末检测试题(含答案解析)
- 2026事业单位笔试-北京-北京放射医学与技术(医疗招聘)历年参考题库含答案详解
- 2026年上海市初中语文第12单元课后练习题
- 2026年《华工综评笔试题目及答案》(含解析)
- 基于智慧教育云平台的家校共育协同模式构建与评价体系研究-以某高中为例教学研究课题报告
- 零壹空间OS火箭战略竞争力分析市场调研报告
- GA 991-2025爆破作业项目管理要求
- 2025年度苏州城际铁路有限公司管理岗位公开招聘笔试参考题库附带答案详解
- 2026年中央一号文件解读:农业机械化水平提升
- 中国华能集团有限公司行测笔试题库2026
- 高职司法口才课件
- 尿毒症并发症处理
- 2025年锂电池隔膜材料成本构成分析报告
- 乳腺癌案例教学教案
- 2025淘宝Weex跨多端业务高效交付实践
评论
0/150
提交评论