java基础学习第8章.ppt

上传人:max****ui 文档编号:8619311 上传时间:2020-03-30 格式:PPT 页数:74 大小:447.50KB
返回 下载 相关 举报
java基础学习第8章.ppt_第1页
第1页 / 共74页
java基础学习第8章.ppt_第2页
第2页 / 共74页
java基础学习第8章.ppt_第3页
第3页 / 共74页
点击查看更多>>
资源描述
第八章线程 郑莉 JAVA语言程序设计 2 目录 多线程编程基础线程的生命周期线程的优先级本章小结 3 8 1多线程编程基础 本节内容线程的概念Thread类Runnable接口线程间的数据共享多线程的同步控制线程之间的通信后台线程 4 8 1 1线程的概念 进程和线程的区别进程一个独立程序的每一次运行称为一个进程 例如用字处理软件编辑文稿时 同时打开mp3播放程序听音乐 这两个独立的程序在同时运行 称为两个进程设置一个进程要占用相当一部分处理器时间和内存资源大多数操作系统不允许进程访问其他进程的内存空间 进程间的通信很不方便 编程模型比较复杂 多线程编程基础 5 线程一个程序中多段代码同时并发执行 称为多线程通过多线程 一个进程表面上看同时可以执行一个以上的任务 并发创建线程比创建进程开销要小得多 线程之间的协作和数据交换也比较容易Java是第一个支持内置线程操作的主流编程语言多数程序设计语言支持多线程要借助于操作系统 原语 primitives 8 1 1线程的概念 续 多线程编程基础 6 8 1 2Thread类 Thread类在Java程序中创建多线程的方法之一是继承Thread类封装了Java程序中一个线程对象需要拥有的属性和方法从Thread类派生一个子类 并创建这个子类的对象 就可以产生一个新的线程 这个子类应该重写Thread类的run方法 在run方法中写入需要在新线程中执行的语句段 这个子类的对象需要调用start方法来启动 新线程将自动进入run方法 原线程将同时继续往下执行Thread类直接继承了Object类 并实现了Runnable接口 它位于java lang包中 因而程序开头不用import任何包就可直接使用 多线程编程基础 7 8 1 2Thread类 续 例8 1 在新线程中完成计算某个整数的阶乘publicclassEx8 1 publicstaticvoidmain String args System out println mainthreadstarts FactorialThreadthread newFactorialThread 10 thread start System out println mainthreadends classFactorialThreadextendsThread privateintnum publicFactorialThread intnum this num num 多线程编程基础 8 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 运行结果mainthreadstartsmainthreadendsnewthreadstartedThefactorialof10is3628800newthreadends 8 1 2Thread类 续 例8 1运行结果 多线程编程基础 9 结果说明main线程已经执行完后 新线程才执行完main函数调用thread start 方法启动新线程后并不等待其run方法返回就继续运行 thread run函数在一边独自运行 不影响原来的main函数的运行源程序修改如果启动新线程后希望主线程多持续一会再结束 可在start语句后加上让当前线程 这里当然是main 休息1毫秒的语句 try Thread sleep 1 catch Exceptione 8 1 2Thread类 续 例8 1修改 多线程编程基础 10 修改后运行结果mainthreadstartsnewthreadstaredThefactorialof10is3628800newthreadendsmainthreadends运行结果说明新线程结束后main线程才结束 8 1 2Thread类 续 例8 1修改后运行结果 多线程编程基础 11 8 1 2Thread类 续 常用API函数 多线程编程基础 12 8 1 2Thread类 续 常用API函数 多线程编程基础 13 8 1 2Thread类 续 常用API函数 多线程编程基础 14 创建3个新线程 每个线程睡眠一段时间 0 6秒 然后结束publicclassEx8 2 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 8 1 2Thread类 续 例8 2 多线程编程基础 15 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类 续 例8 2 多线程编程基础 16 运行结果StartingthreadsThreadsstarted mainendsthread1goingtosleepfor3519thread2goingtosleepfor1689thread3goingtosleepfor5565thread2finishedthread1finishedthread3finished说明由于线程3休眠时间最长 所以最后结束 线程2休眠时间最短 所以最先结束每次运行 都会产生不同的随机休眠时间 所以结果都不相同 8 1 2Thread类 续 例8 2运行结果 多线程编程基础 17 8 1 3Runnable接口 Runnable接口Java多线程机制的一个重要部分 实际上它只有一个run 方法Thread类实现了Runnable接口 相对于Thread类 它更适合于多个线程处理同一资源实现Runnable接口的类的对象可以用来创建线程 这时start方法启动此线程就会在此线程上运行run 方法在编写复杂程序时相关的类可能已经继承了某个基类 而Java不支持多继承 在这种情况下 便需要通过实现Runnable接口来生成多线程 多线程编程基础 18 使用Runnable接口实现例8 1功能publicclassEx8 1 publicstaticvoidmain String args System out println mainthreadstarts FactorialThreadt newFactorialThread 10 newThread t start System out println newthreadstarted mainthreadends 8 1 3Runnable接口 续 例8 3 多线程编程基础 19 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接口 续 例8 3 多线程编程基础 20 使用Runnable接口实现例8 2功能publicclassEx8 4 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 8 1 3Runnable接口 续 例8 4 多线程编程基础 21 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 1 3Runnable接口 续 例8 4 多线程编程基础 22 8 1 4线程间的数据共享 代码共享多个线程的执行代码来自同一个类的run方法时 即称它们共享相同的代码数据共享当共享访问相同的对象时 即它们共享相同的数据使用Runnable接口可以轻松实现多个线程共享相同数据 只要用同一个实现了Runnable接口的实例作为参数创建多个线程就可以了 多线程编程基础 23 修改例8 4 只用一个Runnable类型的对象为参数创建3个新线程 publicclassEx8 5 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 8 1 4线程间的数据共享 续 例8 5 多线程编程基础 24 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 1 4线程间的数据共享 续 例8 5 多线程编程基础 25 运行结果StartingthreadsThread1goingtosleepfor966Thread2goingtosleepfor966Threadsstarted mainendsThread3goingtosleepfor966Thread1finishedThread2finishedThread3finished说明因为是用一个Runnable类型对象创建的3个新线程 这三个线程就共享了这个对象的私有成员sleepTime 在本次运行中 三个线程都休眠了966毫秒 8 1 4线程间的数据共享 续 例8 5运行结果 多线程编程基础 26 8 1 4线程间的资源共享 续 独立的同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作例如生产 消费问题 生产线程产生数据流 然后这些数据流再被消费线程消费假设一个Java应用程序 其中有一个线程负责往文件写数据 另一个线程从同一个文件中往出都数据 因为涉及到同一个资源 这里是同一个文件 这两个线程必须保证某种方式的同步 多线程编程基础 27 用三个线程模拟三个售票口 总共出售200张票用3个线程模仿3个售票口的售票行为这3个线程应该共享200张票的数据publicclassEx8 6 publicstaticvoidmain String args SellTicketst newSellTickets newThread t start newThread t start newThread t start 多线程编程基础 8 1 4线程间的数据共享 续 例8 6 28 classSellTicketsimplementsRunnable privateinttickets 200 publicvoidrun while tickets 0 System out println Thread currentThread getName issellingticket tickets 8 1 4线程间的数据共享 续 例8 6 多线程编程基础 29 运行结果选最后几行如下Thread 2issellingticket6Thread 1issellingticket5Thread 0issellingticket4Thread 2issellingticket3Thread 1issellingticket2Thread 0issellingticket1说明在这个例子中 创建了3个线程 每个线程调用的是同一个SellTickets对象中的run 方法 访问的是同一个对象中的变量 tickets 如果是通过创建Thread类的子类来模拟售票过程 再创建3个新线程 则每个线程都会有各自的方法和变量 虽然方法是相同的 但变量却是各有200张票 因而结果将会是各卖出200张票 和原意就不符了 8 1 4线程间的数据共享 续 例8 6运行结果 多线程编程基础 30 8 1 5多线程的同步控制 有时线程之间彼此不独立 需要同步线程间的互斥同时运行的几个线程需要共享一个 些 数据一个线程对共享的数据进行操作时 不允许其他线程打断它 否则会破坏数据的完整性 即被多个线程共享的数据 在某一时刻只允许一个线程对其进行操作 生产者 消费者 问题生产者产生数据 消费者消费数据 具体来说 假设有一个Java应用程序 其中有一个线程负责往数据区写数据 另一个线程从同一数据区中读数据 两个线程可以并行执行 类似于流水线上的两道工序 如果数据区已满 生产者要等消费者取走一些数据后才能再放 而当数据区没有数据时 消费者要等生产者放入一些数据后再取 多线程编程基础 31 用两个线程模拟存票 售票过程假定开始售票处并没有票 一个线程往里存票 另外一个线程则往出卖票我们新建一个票类对象 让存票和售票线程都访问它 本例采用两个线程共享同一个数据对象来实现对同一份数据的操作publicclassEx8 7 publicstaticvoidmain String args Ticketst newTickets 10 newConsumer t start newProducer t start 8 1 5多线程的同步控制 续 例8 7 多线程编程基础 32 classTickets intnumber 0 票号intsize 总票数booleanavailable false 表示目前是否有票可售publicTickets intsize 构造函数 传入总票数参数 this size size 8 1 5多线程的同步控制 续 例8 7 多线程编程基础 33 classProducerextendsThread Ticketst null publicProducer Ticketst this t t publicvoidrun while t number t size System out println Producerputsticket t number t available true 8 1 5多线程的同步控制 续 例8 7 多线程编程基础 34 classConsumerextendsThread 售票线程 Ticketst null inti 0 publicConsumer Ticketst this t t publicvoidrun while i t size if t available true 8 1 5多线程的同步控制 续 例8 7 多线程编程基础 35 运行结果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10Consumerbuysticket9Consumerbuysticket10 通过让两个线程操纵同一个票类对象 实现了数据共享的目的 8 1 5多线程的同步控制 续 例8 7运行结果 多线程编程基础 36 设想一下 假如售票线程运行到t available false之前 CPU切换到存票线程 存票线程将available置为true 并直到整个存票线程结束 再次切换到售票线程后 售票线程执行t available false 此时售票号小于存票数 且存票线程已经结束不再能将t available置为true 则售票线程陷入了死循环如果我们在t available false之前加上sleep语句 让售票线程多停留一会 则可以更加清楚地看到这个问题if i t number try Thread sleep 1 catch InterruptedExceptionexception t available false 8 1 5多线程的同步控制 续 例8 7修改 多线程编程基础 37 修改后运行结果Producerputsticket1Producerputsticket2Producerputsticket3Producerputsticket4Producerputsticket5Producerputsticket6Producerputsticket7Producerputsticket8Consumerbuysticket1Consumerbuysticket2Consumerbuysticket3Consumerbuysticket4Consumerbuysticket5Consumerbuysticket6Consumerbuysticket7Consumerbuysticket8Producerputsticket9Producerputsticket10 8 1 5多线程的同步控制 续 例8 7修改后运行结果 多线程编程基础 38 如何避免上面这种意外 让我们的程序是 线程安全 的呢 解决线程的同步 互斥问题存票线程和售票线程应保持互斥关系 即售票线程执行时不进入存票线程 存票线程执行时不进入售票线程Java使用的同步机制是监视器每个对象都只有一个 锁旗标 与之相连 利用多线程对其的争夺可实现线程间的互斥操作当线程A获得了一个对象的锁旗标后 线程B必须等待线程A完成规定的操作 并释放出锁旗标后 才能获得该对象的锁旗标 并执行线程B中的操作 8 1 5多线程的同步控制 续 解决例8 7的问题 多线程编程基础 39 线程同步的概念 包括互斥和协作互斥 许多线程在同一个共享数据上操作而互不干扰 同一时刻只能有一个线程访问该共享数据 因此有些方法或程序段在同一时刻只能被一个线程执行 称之为监视区协作 多个线程可以有条件地同时操作共享数据 执行监视区代码的线程在条件满足的情况下可以允许其它线程进入监视区 8 1 5多线程的同步控制 续 线程同步 Synchronization 多线程编程基础 40 synchronized 线程同步关键字用于指定需要同步的代码段或方法 也就是监视区可实现与一个锁旗标的交互 例如 synchronized 对象 代码段 synchronized的功能是 首先判断对象的锁旗标是否在 如果在就获得锁旗标 然后就可以执行紧随其后的代码段 如果对象的锁旗标不在 已被其他线程拿走 就进入等待状态 直到获得锁旗标当被synchronized限定的代码段执行完 就释放锁旗标 8 1 5多线程的同步控制 续 synchronized关键字 多线程编程基础 41 将需要互斥的语句段放入synchronized object 语句框中 且两处的object是相同的classProducerextendsThread Ticketst null publicProducer Ticketst this t t publicvoidrun while t number t size synchronized t 申请对象t的锁旗标System out println Producerputsticket t number t available true 释放对象t的锁旗标 System out println Producerends 8 1 5多线程的同步控制 续 synchronized关键字 多线程编程基础 42 classConsumerextendsThread Ticketst null inti 0 publicConsumer Ticketst this t t publicvoidrun while i t size synchronized t 申请对象t的锁旗标if t available true 8 1 5多线程的同步控制 续 synchronized关键字 多线程编程基础 43 说明存票程序段和售票程序段为获得同一对象的锁旗标而实现互斥操作当线程执行到synchronized的时候 检查传入的实参对象 并申请得到该对象的锁旗标 如果得不到 那么线程就被放到一个与该对象锁旗标相对应的等待线程池中 直到该对象的锁旗标被归还 池中的等待线程才能重新去获得锁旗标 然后继续执行下去除了可以对指定的代码段进行同步控制之外 还可以定义整个方法在同步控制下执行 只要在方法定义前加上synchronized关键字即可 8 1 5多线程的同步控制 续 synchronized关键字 多线程编程基础 44 实现例8 7功能 将互斥方法放在共享的资源类Tickets中classTickets intsize 票总数intnumber 0 存票序号inti 0 售票序号booleanavailable false 是否有待售的票publicTickets intsize this size size publicsynchronizedvoidput 同步方法 实现存票的功能System out println Producerputsticket number available true publicsynchronizedvoidsell 同步方法 实现售票的功能if available true 8 1 5多线程的同步控制 续 例8 8 多线程编程基础 45 说明同步方法使用的锁旗标关联对象正是方法所属的实例对象 在例8 8中 正是因为put和sell两个同步方法都属于同一个Tickets类的对象 所以实现了同步由于要实现多线程的数据共享 即多个线程对同一数据资源进行操作 就可能造成一个线程对资源进行了部分处理 另一个线程就插进来对其进行处理 这样就会破坏共享数据的完整性 因此 需要使用线程同步与互斥技术 防止不同的线程同时对共享数据进行修改操作 数据共享和线程互斥操作经常是密不可分的 8 1 5多线程的同步控制 续 例8 8说明 多线程编程基础 46 8 1 6线程之间的通信 为了更有效地协调不同线程的工作 需要在线程间建立沟通渠道 通过线程间的 对话 来解决线程间的同步问题java lang Object类的一些方法为线程间的通讯提供了有效手段wait 如果当前状态不适合本线程执行 正在执行同步代码 synchronized 的某个线程A调用该方法 在对象x上 该线程暂停执行而进入对象x的等待池 并释放已获得的对象x的锁旗标 线程A要一直等到其他线程在对象x上调用notify或notifyAll方法 才能够在重新获得对象x的锁旗标后继续执行 从wait语句后继续执行 多线程编程基础 47 8 1 6线程之间的通信 续 notify 和notifyAll 方法 notify 随机唤醒一个等待的线程 本线程继续执行线程被唤醒以后 还要等发出唤醒消息者释放监视器 这期间关键数据仍可能被改变被唤醒的线程开始执行时 一定要判断当前状态是否适合自己运行notifyAll 唤醒所有等待的线程 本线程继续执行 多线程编程基础 48 修改例8 8 使每存入一张票 就售一张票 售出后 再存入classTickets publicsynchronizedvoidput if available 如果还有存票待售 则存票线程等待try wait catch Exceptione System out println Producerputsticket number available true notify 存票后唤醒售票线程开始售票 publicsynchronizedvoidsell if available 如果没有存票 则售票线程等待try wait catch Exceptione System out println Consumerbuysticket number available false notify 售票后唤醒存票线程开始存票if number size number size 1 在售完最后一张票后 设置一个结束标志 number size表示售票结束 8 1 6线程之间的通信 续 例8 9 多线程编程基础 49 classProducerextendsThread Ticketst null publicProducer Ticketst this t t publicvoidrun while t number t size t put classConsumerextendsThread Ticketst null publicConsumer Ticketst this t t publicvoidrun while t number t size t sell 8 1 6线程之间的通信 续 例8 9 多线程编程基础 50 运行结果Producerputsticket1Consumerbuysticket1Producerputsticket2Consumerbuysticket2Producerputsticket3Consumerbuysticket3Producerputsticket4Consumerbuysticket4Producerputsticket5Consumerbuysticket5Producerputsticket6Consumerbuysticket6Producerputsticket7Consumerbuysticket7Producerputsticket8Consumerbuysticket8Producerputsticket9Consumerbuysticket9Producerputsticket10Consumerbuysticket10 8 1 6线程之间的通信 续 例8 9运行结果 多线程编程基础 51 程序说明当Consumer线程售出票后 available值变为false 当Producer线程放入票后 available值变为true只有available为true时 Consumer线程才能售票 否则就必须等待Producer线程放入新的票后的通知只有available为false时 Producer线程才能放票 否则必须等待Consumer线程售出票后的通知可见通过线程间的通信实现了我们的要求 8 1 6线程之间的通信 续 例8 9说明 多线程编程基础 52 8 1 7后台线程 后台线程也叫守护线程 通常是为了辅助其它线程而运行的线程它不妨碍程序终止一个进程中只要还有一个前台线程在运行 这个进程就不会结束 如果一个进程中的所有前台线程都已经结束 那么无论是否还有未结束的后台线程 这个进程都会结束 垃圾回收 便是一个后台线程如果对某个线程对象在启动 调用start方法 之前调用了setDaemon true 方法 这个线程就变成了后台线程 多线程编程基础 53 创建一个无限循环的后台线程 验证主线程结束后 程序即结束publicclassEx8 10 publicstaticvoidmain String args ThreadTestt newThreadTest t setDaemon true t start classThreadTestextendsThread publicvoidrun while true 运行程序 则发现整个程序在主线程结束时就随之中止运行了 如果注释掉t setDaemon true 语句 则程序永远不会结束 8 1 7后台线程 续 例8 10 多线程编程基础 54 8 2线程的生命周期 线程的生命周期线程从产生到消亡的过程一个线程在任何时刻都处于某种线程状态 threadstate 55 8 2 1线程的几种基本状态 线程生命周期状态图 线程的生命周期 56 诞生状态线程刚刚被创建就绪状态线程的start方法已被执行线程已准备好运行运行状态处理机分配给了线程 线程正在运行阻塞状态 Blocked 在线程发出输入 输出请求且必须等待其返回遇到用synchronized标记的方法而未获得其监视器暂时不能进入执行时休眠状态 Sleeping 执行sleep方法而进入休眠死亡状态线程已完成或退出 8 2 1线程的几种基本状态 续 线程的生命周期 57 8 2 2死锁问题 死锁线程在运行过程中 其中某个步骤往往需要满足一些条件才能继续进行下去 如果这个条件不能满足 线程将在这个步骤上出现阻塞线程A可能会陷于对线程B的等待 而线程B同样陷于对线程C的等待 依次类推 整个等待链最后又可能回到线程A 如此一来便陷入一个彼此等待的轮回中 任何线程都动弹不得 此即所谓死锁 deadlock 对于死锁问题 关键不在于出现问题后调试 而是在于预防 线程的生命周期 58 设想一个游戏 规则为3个人站在三角形的三个顶点的位置上 三个边上放着三个球 如图所示 每个人都必须先拿到自己左手边的球 才能再拿到右手边的球 两手都有球之后 才能够把两个球都放下 Player 0 Player 1 Player 2 0 2 1 8 2 2死锁问题 续 例8 11 线程的生命周期 59 例8 11创建3个线程模拟3个游戏者的行为 publicclassEx8 11 publicstaticvoidmain String args Ballsball newBalls 新建一个球类对象Player0p0 newPlayer0 ball 创建0号游戏者Player1p1 newPlayer1 ball 创建1号游戏者Player2p2 newPlayer2 ball 创建2号游戏者p0 start 启动0号游戏者p1 start 启动1号游戏者p2 start 启动2号游戏者 classBalls 球类booleanflag0 false 0号球的标志变量 true表示已被人拿 false表示未被任何人拿booleanflag1 false 1号球的标志变量booleanflag2 false 2号球的标志变量 8 2 2死锁问题 续 例8 11 线程的生命周期 60 classPlayer0extendsThread 0号游戏者的类privateBallsball publicPlayer0 Ballsb this ball b publicvoidrun while true while ball flag1 true 如果1号球已被拿走 则等待ball flag1 true 拿起1号球while ball flag0 true 如果0号球已被拿走 则等待if ball flag1 true 放下后休息1ms 8 2 2死锁问题 续 例8 11 线程的生命周期 61 运行结果若干次后将陷入死锁 不再有输出信息 即任何人都不能再同时拥有两侧的球程序说明如果刚好3个人都拿到了左手边的球 都等待那右手边的球 则因为谁都不能放手 则这3个线程都将陷入无止尽的等待当中 这就构成了死锁为了便于观察死锁发生的条件 我们在每个游戏者放下两边的球后增加了sleep语句为了避免死锁 需要修改游戏规则 使每个人都只能先抢到两侧中号比较小的球 才能拿另一只球 这样就不会再出现死锁现象 8 2 2死锁问题 续 例8 11运行结果 线程的生命周期 62 8 2 3控制线程的生命 结束线程的生命用stop方法可以结束线程的生命但如果一个线程正在操作共享数据段 操作过程没有完成就用stop结束的话 将会导致数据的不完整 因此并不提倡使用此方法通常 可通过控制run方法中循环条件的方式来结束一个线程 线程的生命周期 63 线程不断显示递增整数 按下回车键则停止执行importjava io publicclassEx8 12 publicstaticvoidmain String args throwsIOException TestThreadt newTestThread t start newBufferedReader newInputStreamReader System in readLine 等待键盘输入t stopme 调用stopme方法结束t线程 8 2 3控制线程的生命 续 例8 12 线程的生命周期 64 classTestThreadextendsThread privatebooleanflag true publicvoidstopme 在此方法中控制循环条件flag false publicvoidrun inti 0 while flag System out println i 如果flag为真则一直显示递增整数 运行效果为按下回车键后则停止显示 8 2 3控制线程的生命 续 例8 12 线程的生命周期 65 8 3线程的优先级 线程调度在单CPU的系统中 多个线程需要共享CPU 在任何时间点上实际只能有一个线程在运行控制多个线程在同一个CPU上以某种顺序运行称为线程调度Java虚拟机支持一种非常简单的 确定的调度算法 叫做固定优先级算法 这个算法基于线程的优先级对其进行调度 66 线程的优先级每个Java线程都有一个优先级 其范围都在1和10之间 默认情况下 每个线程的优先级都设置为5在线程A运行过程中创建的新的线程对象B 初始状态具有和线程A相同的优先级如果A是个后台线程 则B也是个后台线程可在线程创建之后的任何时候 通过setPriority intpriority 方法改变其原来的优先级 8 3线程的优先级 续 线程的优先级 67 基于线程优先级的线程调度具有较高优先级的线程比优先级较低的线程优先执行对具有相同优先级的线程 Java的处理是随机的底层操作系统支持的优先级可能要少于10个 这样会造成一些混乱 因此 只能将优先级作为一种很粗略的工具使用 最后的控制可以通过明智地使用yield 函数来完成我们只能基于效率的考虑来使用线程优先级 而不能依靠线程优先级来保证算法的正确性 8 3线程的优先级 续 线程的优先级 68 假设某线程正在运行 则只有出现以下情况之一 才会使其暂停运行一个具有更高优先级的线程变为就绪状态 Ready 由于输入 输出 或其他一些原因 调用sleep wait yield方法使其发生阻塞 对于支持时间分片的系统 时间片的时间期满 8 3线程的优先级 续 线程的优先级 69 创建两个具有不同优先级的线程 都从1递增到400000 每增加50000显示一次publicclassEx8 13 publicstaticvoidmain String args TestThread runners newTestThread 2 for inti 0 i 2 i runners i newTestThread i runners 0 setPriority 2 设置第一个线程优先级为2runners 1 setPriority 3 设置第二个线程优先级为3for inti 0 i 2 i runners i start 8 3线程的优先级 续 例8 13 线程的优先级 70 classTestThreadextendsThread privateinttick 1 privateintnum publicTestThread inti this num i publicvoidrun while tick 400000 tick if tick 50000 0 每隔5000进行显示System out println Thread num tick tick yield 放弃执行权 8 3线程的优先级 续 例8 13 线程的优先级 71 8 3线程的优先级 续 例8 13运行结果 运行结果Thread 1 tick 50000Thread 1 tick 100000Thread 1 tick 150000Thread 1 tick 200000Thread 1 tick 250000Thread 1 tick 300000Thread 1 tick 350000Thread 1 tick 400000Thread 0 tick 50000Thread 0 tick 100000Thread 0 tick 150000Thread 0 tick 200000Thread 0 tick 250000Thread 0 tick 300000Thread 0 tick 350000Thread 0 tick 400000 结果说明具有较高优先级的线程1一直运行到结束 具有较低优先级的线程0才开始运行虽然具有较高优先级的线程1调用了yield方法放弃CPU资源 允许线程0进行争夺 但马上又被线程1抢夺了回去 所以有没有yield方法都没什么区别 线程的优先级 72 如果在yield方法后增加一行sleep语句 让线程1暂时放弃一下在CPU上的运行 哪怕是1毫秒 则线程0也可以有机会被调度 修改后的run方法如下publicvoidrun while tick 400000 tick if tick 50000 0 System out println Thread num tick tick yield try sleep 1 catch Exceptione 8 3线程的优先级 续 例8 13修改 线程的优先级 73 8 3线程的优先级 续 例8 13修改后运行结果 运行结果Thread 1 tick 50000Thread 1 tick 100000Thread 1 tick 150000Thread 1 tick 200000Thread 0 tick 50000Thread 1 tick 250000Thread 1 tick 300000Thread 0 tick 100000Thread 1 tick 350000Thread 1 tick 400000Thread 0 tick 150000Thread 0 tick 200000Thread 0 tick 250000Thread 0 tick 300000Thread 0 tick 350000Thread 0 tick 400000 说明具有较低优先权的线程0在线程1没有执行完毕前也获得了一部分执行 但线程1还是优先完成了执行Java虚拟机本身并不支持某个线程抢夺另一个正在执行的具有同等优先级线程的执行权通常 我们在一个线程内部插入yield 语句 这个方法会使正在运行的线程暂时放弃执行 这是具有同样优先级的线程就有机会获得调度开始运行 但较低优先级的线程仍将被忽略不参加调度 线程的优先级 74 8 4本章小结 本章内容线程的基础知识线程的生命周期线程的优先级本章要求了解线程的概念学会如何通过Thread类和Runnable接口创建线程 如何实现多线程的资源共享和通信 及如何控制线程的生命掌握线程同步的方法理解线程优先级的概念 以及基于优先级的线程调度
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!