Java学习第8章

上传人:da****ge 文档编号:243096190 上传时间:2024-09-15 格式:PPT 页数:74 大小:711.50KB
返回 下载 相关 举报
Java学习第8章_第1页
第1页 / 共74页
Java学习第8章_第2页
第2页 / 共74页
Java学习第8章_第3页
第3页 / 共74页
点击查看更多>>
资源描述
,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,单击此处编辑母版标题样式,第八章 线程,郑 莉,JAVA,语言程序设计,2,目录,多线程编程基础,线程的生命周期,线程的优先级,本章小结,3,8.1,多线程编程基础,本节内容,线程的概念,Thread,类,Runnable,接口,线程间的数据共享,多线程的同步控制,线程之间的通信,后台线程,4,8.1.1,线程的概念,进程和线程的区别,进程,一个独立程序的每一次运行称为一个进程,例如,用字处理软件编辑文稿时,同时打开,mp3,播放程序听音乐,这两个独立的程序在同时运行,称为两个进程,设置一个进程要占用相当一部分处理器时间和内存资源,大多数操作系统不允许进程访问其他进程的内存空间,进程间的通信很不方便,编程模型比较复杂,多线程编程基础,5,线程,一个程序中多段代码同时并发执行,称为多线程,通过多线程,一个,进程,表面上看同时可以执行一个以上的任务,并发,创建线程比创建进程开销要小得多,线程之间的协作和数据交换也比较容易,Java,是第一个支持内置线程操作的主流编程语言,多数程序设计语言支持多线程要借助于操作系统“原语,(,primitives,),”,8.1.1,线程的概念,(,续,),多线程编程基础,6,8.1.2 Thread,类,Thread,类,在,Java,程序中创建多线程的方法之一是继承,Thread,类,封装了,Java,程序中一个线程对象需要拥有的属性和方法,从,Thread,类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写,Thread,类的,run,方法,在,run,方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用,start,方法来启动,新线程将自动进入,run,方法。原线程将同时继续往下执行,Thread,类直接继承了,Object,类,并实现了,Runnable,接口。它位于,java.lang,包中,因而程序开头不用,import,任何包就可直接使用,多线程编程基础,7,8.1.2 Thread,类,(,续,),例,8_1,在新线程中完成计算某个整数的阶乘,public class Ex8_1 ,public static void main( String args ) ,System.out.println(main thread starts);,FactorialThread thread=new FactorialThread(10);,thread.start();,System.out.println(main thread ends );,class FactorialThread,extends,Thread,private int num;,public FactorialThread( int num ),this.num=num;,多线程编程基础,8,public void run() ,int i=num;,int result=1;,System.out.println(new thread started );,while(i0) ,result=result*i;,i=i-1;,System.out.println(The factorial of +num+ is +result);,System.out.println(new thread ends);,运行结果,main thread starts,main thread ends,new thread started,The factorial of 10 is 3628800,new thread ends,8.1.2 Thread,类,(,续,),例,8_1,运行结果,多线程编程基础,9,结果说明,main,线程已经执行完后,新线程才执行完,main,函数调用,thread.start(),方法启动新线程后并不等待其,run,方法返回就继续运行,,thread.run,函数在一边独自运行,不影响原来的,main,函数的运行,源程序修改,如果启动新线程后希望主线程多持续一会再结束,可在,start,语句后加上让当前线程(这里当然是,main,)休息,1,毫秒的语句:,try Thread.sleep(1); catch(Exception e);,8.1.2 Thread,类,(,续,),例,8_1,修改,多线程编程基础,10,修改后运行结果,main thread starts,new thread stared,The factorial of 10 is 3628800,new thread ends,main thread ends,运行结果说明,新线程结束后,main,线程才结束,8.1.2 Thread,类,(,续,),例,8_1,修改后运行结果,多线程编程基础,11,8.1.2 Thread,类,(,续,),常用,API,函数,名称,说明,public,Thread(),构造一个新的线程对象,默认名为,Thread-n,,,n,是从,0,开始递增的整数,public,Thread(Runnable target),构造一个新的线程对象,以一个实现,Runnable,接口的类的对象为参数。默认名为,Thread-n,,,n,是从,0,开始递增的整数,public,Thread(String name),构造一个新的线程对象,并同时指定线程名,public static Thread currentThread(),返回当前正在运行的线程对象,public static void yield(),使当前线程对象暂停,允许别的线程开始运行,public static void,sleep(long millis),使当前线程暂停运行指定毫秒数,但此线程并不失去已获得的锁旗标。,多线程编程基础,12,public void,start(),启动线程,,JVM,将调用此线程的,run,方法,结果是将同时运行两个线程,当前线程和执行,run,方法的线程,public void,run(),Thread,的子类应该重写此方法,内容应为该线程应执行的任务。,public final void stop(),停止线程运行,释放该线程占用的对象锁旗标。,public void interrupt(),打断此线程,public final void join(),在当前线程中加入调用,join,方法的线程,A,,直到线程,A,死亡才能继续执行当前线程,public final void join(long millis),在当前线程中加入调用,join,方法的线程,A,,直到到达参数指定毫秒数或线程,A,死亡才能继续执行当前线程,8.1.2 Thread,类,(,续,),常用,API,函数,多线程编程基础,13,public final void setPriority(,int newPriority),设置线程优先级,public final void setDaemon(Boolean on),设置是否为后台线程,如果当前运行线程均为后台线程则,JVM,停止运行。这个方法必须在,start(),方法前使用,public final void checkAccess(),判断当前线程是否有权力修改调用此方法的线程,public void setName(String name),更该本线程的名称为指定参数,public final boolean isAlive(),测试线程是否处于活动状态,如果线程被启动并且没有死亡则返回,true,8.1.2 Thread,类,(,续,),常用,API,函数,多线程编程基础,14,创建,3,个新线程,每个线程睡眠一段时间(,0,6,秒),然后结束,public class Ex8_2 ,public static void main( String args ) ,/,创建并命名每个线程,TestThread thread1 = new TestThread( thread1 );,TestThread thread2 = new TestThread( thread2 );,TestThread thread3 = new TestThread( thread3 );,System.out.println( Starting threads );,thread1.start(); /,启动线程,1,thread2.start(); /,启动线程,2,thread3.start(); /,启动线程,3,System.out.println( Threads started, main endsn );,8.1.2 Thread,类,(,续,),例,8_2,多线程编程基础,15,class TestThread,extends Thread,private int sleepTime;,public TestThread( String name ) ,super( name );,sleepTime = ( int ) ( Math.random() * 6000 );,public void run() ,try ,System.out.println(,getName() + going to sleep for + sleepTime );,Thread.sleep( sleepTime ); /,线程休眠,catch ( InterruptedException exception ) ;,System.out.println( getName() + finished,8.1.2 Thread,类,(,续,),例,8_2,多线程编程基础,16,运行结果,Starting threads,Threads started, main ends,thread1 going to sleep for 3519,thread2 going to sleep for 1689,thread3 going to sleep for 5565,thread2 finished,thread1 finished,thread3 finished,说明,由于线程,3,休眠时间最长,所以最后结束,线程,2,休眠时间最短,所以最先结束,每次运行,都会产生不同的随机休眠时间,所以结果都不相同,8.1.2 Thread,类,(,续,),例,8_2,运行结果,多线程编程基础,17,8.1.3 Runnable,接口,Runnable,接口,Java,多线程机制的一个重要部分,实际上它只有一个,run(),方法,Thread,类实现了,Runnable,接口,,相对于,Thread,类,它更适合于多个线程处理同一资源,实现,Runnable,接口的类的对象可以用来创建线程,这时,start,方法启动此线程就会在此线程上运行,run(),方法,在编写复杂程序时相关的类可能已经继承了某个基类,而,Java,不支持多继承,在这种情况下,便需要通过实现,Runnable,接口来生成多线程,多线程编程基础,18,使用,Runnable,接口实现例,8_1,功能,public class Ex8_1,public static void main( String args ) ,System.out.println(main thread starts);,FactorialThread t=new FactorialThread(10);,new Thread(t).start();,System.out.println(new thread started,main thread ends );,8.1.3 Runnable,接口,(,续,),例,8_3,多线程编程基础,19,class FactorialThread implements Runnable ,private int num;,public FactorialThread( int num ),this.num=num;,public void run() ,int i=num;,int result=1;,while(i0) ,result=result*i;,i=i-1;,System.out.println(The factorial of +num+ is +result);,System.out.println(new thread ends);,8.1.3 Runnable,接口,(,续,),例,8_3,多线程编程基础,20,使用,Runnable,接口实现例,8_2,功能,public class Ex8_4,public static void main( String args ) ,TestThread thread1 = new TestThread();,TestThread thread2 = new TestThread();,TestThread thread3 = new TestThread();,System.out.println( Starting threads );,new Thread(thread1,Thread1).start();,new Thread(thread2,Thread2).start();,new Thread(thread3,Thread3).start();,System.out.println( Threads started, main endsn );,8.1.3 Runnable,接口,(,续,),例,8_4,多线程编程基础,21,class TestThread implements Runnable ,private int sleepTime;,public TestThread(),sleepTime = ( int ) ( Math.random() * 6000 );,public void run() ,try ,System.out.println(,Thread.currentThread().getName(),+ going to sleep for ,+ sleepTime );,Thread.sleep( sleepTime );,catch ( InterruptedException exception ) ;,System.out.println,(,Thread.currentThread().getName(),+ finished );,8.1.3 Runnable,接口,(,续,),例,8_4,多线程编程基础,22,8.1.4,线程间的数据共享,代码共享,多个线程的执行代码来自同一个类的,run,方法时,即称它们共享相同的代码,数据共享,当共享访问相同的对象时,即它们共享相同的数据,使用,Runnable,接口可以轻松实现多个线程共享相同数据,只要用同一个实现了,Runnable,接口的实例作为参数创建多个线程就可以了,多线程编程基础,23,修改例,8_4,,只用一个,Runnable,类型的对象为参数创建,3,个新线程。,public class Ex8_5 ,public static void main( String args ) ,TestThread threadobj = new TestThread();,System.out.println( Starting threads );,new Thread(,threadobj,Thread1).start();,new Thread(threadobj,Thread2).start();,new Thread(threadobj,Thread3).start();,System.out.println( Threads started, main endsn );,8.1.4,线程间的数据共享,(,续,),例,8_5,多线程编程基础,24,class TestThread implements Runnable ,private int sleepTime;,public TestThread(),sleepTime = ( int ) ( Math.random() * 6000 );,public void run(),try ,System.out.println(,Thread.currentThread().getName() + going to sleep for + sleepTime );,Thread.sleep( sleepTime );,catch ( InterruptedException exception ) ;,System.out.println(,Thread.currentThread().getName() + finished );,8.1.4,线程间的数据共享,(,续,),例,8_5,多线程编程基础,25,运行结果,Starting threads,Thread1 going to sleep for 966,Thread2 going to sleep for 966,Threads started, main ends,Thread3 going to sleep for 966,Thread1 finished,Thread2 finished,Thread3 finished,说明,因为是用一个,Runnable,类型对象创建的,3,个新线程,这三个线程就共享了这个对象的私有成员,sleepTime,,在本次运行中,三个线程都休眠了,966,毫秒,8.1.4,线程间的数据共享,(,续,),例,8_5,运行结果,多线程编程基础,26,8.1.4,线程间的资源共享,(,续,),独立的同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作,例如生产,/,消费问题:生产线程产生数据流,然后这些数据流再被消费线程消费,假设一个,Java,应用程序,其中有一个线程负责往文件写数据,另一个线程从同一个文件中往出都数据,因为涉及到同一个资源,这里是同一个文件,这两个线程必须保证某种方式的同步,多线程编程基础,27,用三个线程模拟三个售票口,总共出售,200,张票,用,3,个线程模仿,3,个售票口的售票行为,这,3,个线程应该共享,200,张票的数据,public class Ex8_6,public static void main(String args),SellTickets t=new SellTickets();,new Thread(t).start();,new Thread(t).start();,new Thread(t).start();,多线程编程基础,8.1.4,线程间的数据共享,(,续,),例,8_6,28,class SellTickets implements Runnable,private int tickets=20;,public void run(),while(tickets0),System.out.println( Thread.currentThread().getName() +, is selling ticket +tickets-);,8.1.4,线程间的数据共享,(,续,),例,8_6,多线程编程基础,29,运行结果选最后几行如下,Thread-2 is selling ticket 6,Thread-1 is selling ticket 5,Thread-0 is selling ticket 4,Thread-2 is selling ticket 3,Thread-1 is selling ticket 2,Thread-0 is selling ticket 1,说明,在这个例子中,创建了,3,个线程,每个线程调用的是,同一个,SellTickets,对象,中的,run(),方法,访问的是,同一个对象中的变量(,tickets,),如果是通过创建,Thread,类的子类来模拟售票过程,再创建,3,个新线程,则每个线程都会有各自的方法和变量,虽然方法是相同的,但变量却是,各有,200,张票,,因而结果将会是各卖出,200,张票,和原意就不符了,8.1.4,线程间的数据共享,(,续,),例,8_6,运行结果,多线程编程基础,30,8.1.5,多线程的同步控制,有时线程之间彼此不独立、需要同步,线程间的互斥,同时运行的几个线程需要共享一个(些)数据,一个线程对共享的数据进行操作时,不允许其他线程打断它,否则会破坏数据的完整性。即被多个线程共享的数据,在某一时刻只允许一个线程对其进行操作,“生产者,/,消费者” 问题,生产者产生数据,消费者消费数据,具体来说,假设有一个,Java,应用程序,其中有一个线程负责往数据区写数据,另一个线程从同一数据区中读数据,两个线程可以并行执行(类似于流水线上的两道工序),如果数据区已满,生产者要等消费者取走一些数据后才能再放;而当数据区没有数据时,消费者要等生产者放入一些数据后再取,多线程编程基础,31,用两个线程模拟存票、售票过程,假定开始售票处并没有票,一个线程往里存票,另外一个线程则往出卖票,我们新建一个票类对象,让存票和售票线程都访问它。本例采用两个线程共享同一个数据对象来实现对同一份数据的操作,public class Ex8_7 ,public static void main(String args) ,Tickets t=new Tickets(10);,new Consumer(t).start();,new Producer(t).start();,8.1.5,多线程的同步控制,(,续,),例,8_7,多线程编程基础,32,class Tickets ,int number=0; /,票号,int size; /,总票数,boolean available=false; /,表示目前是否有票可售,public Tickets(int size),/,构造函数,传入总票数参数,this.size=size;,8.1.5,多线程的同步控制,(,续,),例,8_7,多线程编程基础,33,class Producer extends Thread,Tickets t=null;,public Producer(Tickets t),this.t=t;,public void run(),while( t.number t.size),System.out.println(Producer puts ticket ,+(+t.number);,t.available=true;,8.1.5,多线程的同步控制,(,续,),例,8_7,多线程编程基础,34,class Consumer extends Thread /,售票线程,Tickets t=null;,int i=0;,public Consumer(Tickets t), this.t=t;,public void run(),while(it.size),if(t.available=true & i=t.number),System.out.println(Consumer buys ticket +(+i);,if(i=t.number),t.available=false;,8.1.5,多线程的同步控制,(,续,),例,8_7,多线程编程基础,35,运行结果,Producer puts ticket 1,Producer puts ticket 2,Producer puts ticket 3,Producer puts ticket 4,Producer puts ticket 5,Producer puts ticket 6,Producer puts ticket 7,Producer puts ticket 8,Consumer buys ticket 1,Consumer buys ticket 2,Consumer buys ticket 3,Consumer buys ticket 4,Consumer buys ticket 5,Consumer buys ticket 6,Consumer buys ticket 7,Consumer buys ticket 8,Producer puts ticket 9,Producer puts ticket 10,Consumer buys ticket 9,Consumer buys ticket 10.,通过让两个线程操纵同一个票类对象,实现了数据共享的目的,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 ( InterruptedException exception ) ;,t.available=false;,8.1.5,多线程的同步控制,(,续,),例,8_7,修改,多线程编程基础,37,修改后运行结果,Producer puts ticket 1,Producer puts ticket 2,Producer puts ticket 3,Producer puts ticket 4,Producer puts ticket 5,Producer puts ticket 6,Producer puts ticket 7,Producer puts ticket 8,Consumer buys ticket 1,Consumer buys ticket 2,Consumer buys ticket 3,Consumer buys ticket 4,Consumer buys ticket 5,Consumer buys ticket 6,Consumer buys ticket 7,Consumer buys ticket 8,Producer puts ticket 9,Producer puts ticket 10,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,是相同的,class Producer extends Thread ,Tickets t=null;,public Producer(Tickets t) this.t=t;,public void run() ,while(t.number)t.size) ,synchronized(t), /,申请对象,t,的锁旗标,System.out.println(Producer puts ticket +(+t.number);,t.available=true;, /,释放对象,t,的锁旗标,System.out.println(Producer ends!);,8.1.5,多线程的同步控制,(,续,),synchronized,关键字,多线程编程基础,42,class Consumer extends Thread ,Tickets t=null;,int i=0;,public Consumer(Tickets t) this.t=t; ,public void run() ,while(it.size) ,synchronized(t) /,申请对象,t,的锁旗标,if(t.available=true & i=t.number),System.out.println(Consumer buys ticket +(+i); if(i=t.number) ,tryThread.sleep(1);catch(Exception e),t.available=false;,/,释放对象,t,的锁旗标,System.out.println(Consumer ends);,8.1.5,多线程的同步控制,(,续,),synchronized,关键字,多线程编程基础,43,说明,存票程序段和售票程序段为获得同一对象的锁旗标而实现互斥操作,当线程执行到,synchronized,的时候,检查传入的实参对象,并申请得到该对象的锁旗标。如果得不到,那么线程就被放到一个与该对象锁旗标相对应的等待线程池中。直到该对象的锁旗标被归还,池中的等待线程才能重新去获得锁旗标,然后继续执行下去,除了可以对指定的代码段进行同步控制之外,还可以,定义整个方法在同步控制下执行,只要在方法定义前加上,synchronized,关键字即可,8.1.5,多线程的同步控制,(,续,),synchronized,关键字,多线程编程基础,44,实现例,8_7,功能。将互斥方法放在共享的资源类,Tickets,中,class Tickets ,int size; /,票总数,int number=0; /,存票序号,int i=0; /,售票序号,boolean available=false; /,是否有待售的票,public Tickets(int size) this.size=size; ,public synchronized void put(), /,同步方法,实现存票的功能,System.out.println(Producer puts ticket +(+number);,available=true;,public synchronized void sell(), /,同步方法,实现售票的功能,if(available=true & isize,表示售票结束,8.1.6,线程之间的通信,(,续,),例,8_9,多线程编程基础,49,class Producer extends Thread ,Tickets t=null;,public Producer(Tickets t) this.t=t; ,public void run() ,while(t.numbert.size) t.put();,class Consumer extends Thread ,Tickets t=null;,public Consumer(Tickets t) this.t=t; ,public void run() ,while(t.number=t.size) t.sell();,8.1.6,线程之间的通信,(,续,),例,8_9,多线程编程基础,50,运行结果,Producer puts ticket 1,Consumer buys ticket 1,Producer puts ticket 2,Consumer buys ticket 2,Producer puts ticket 3,Consumer buys ticket 3,Producer puts ticket 4,Consumer buys ticket 4,Producer puts ticket 5,Consumer buys ticket 5,Producer puts ticket 6,Consumer buys ticket 6,Producer puts ticket 7,Consumer buys ticket 7,Producer puts ticket 8,Consumer buys ticket 8,Producer puts ticket 9,Consumer buys ticket 9,Producer puts ticket 10,Consumer buys ticket 10,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,创建一个无限循环的后台线程,验证主线程结束后,程序即结束,public class Ex8_10 ,public static void main(String args) ,ThreadTest t=new ThreadTest();,t.setDaemon(true);,t.start();,class ThreadTest extends Thread ,public void run() while(true) ,运行程序,则发现整个程序在主线程结束时就随之中止运行了,如果注释掉,t.setDaemon(true),语句,则程序永远不会结束,8.1.7,后台线程,(,续,),例,8_10,多线程编程基础,54,8.2,线程的生命周期,线程的生命周期,线程从产生到消亡的过程,一个线程在任何时刻都处于某种线程状态(,thread state,),55,8.2.1,线程的几种基本状态,线程生命周期状态图,线程的生命周期,56,诞生状态,线程刚刚被创建,就绪状态,线程的,start,方法已被执行,线程已准备好运行,运行状态,处理机分配给了线程,线程正在运行,阻塞状态(,Blocked,),在线程发出输入,/,输出请求且必须等待其返回,遇到用,synchronized,标记的方法而未获得其监视器暂时不能进入执行时,休眠状态(,Sleeping,),执行,s,leep,方法而进入休眠,死亡状态,线程已完成或退出,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,个游戏者的行为。,public class Ex8_11,public static void main(String args),Balls ball=new Balls(); /,新建一个球类对象,Player0 p0=new Player0(ball); /,创建,0,号游戏者,Player1 p1=new Player1(ball); /,创建,1,号游戏者,Player2 p2=new Player2(ball); /,创建,2,号游戏者,p0.start(); /,启动,0,号游戏者,p1.start(); /,启动,1,号游戏者,p2.start(); /,启动,2,号游戏者,class Balls /,球类,boolean flag0=false; /0,号球的标志变量,,true,表示已被人拿,,false,表示未被任何人拿,boolean flag1=false; /1,号球的标志变量,boolean flag2=false; /2,号球的标志变量,8.2.2,死锁问题,(,续,),例,8_11,线程的生命周期,60,class Player0 extends Thread /0,号游戏者的类,private Balls ball;,public Player0(Balls b), this.ball=b; ,public void run() ,while(true) ,while(ball.flag1=true); /,如果,1,号球已被拿走,则等待,ball.flag1=true; /,拿起,1,号球,while(ball.flag0=true); /,如果,0,号球已被拿走,则等待,if(ball.flag1=true & ball.flag0=false),ball.flag0=true; /,拿起,0,号球,System.out.println(Player0 has got two balls!);,ball.flag1=false; /,放下,1,号球,ball.flag0=false; /,放下,0,号球,try sleep(1);catch(Exception e); /,放下后休息,1ms,8.2.2,死锁问题,(,续,),例,8_11,线程的生命周期,61,运行结果,若干次后将陷入死锁,不再有输出信息,即任何人都不能再同时拥有两侧的球,程序说明,如果刚好,3,个人都拿到了左手边的球,都等待那右手边的球,则因为谁都不能放手,则这,3,个线程都将陷入无止尽的等待当中,这就构成了死锁,为了便于观察死锁发生的条件,我们在每个游戏者放下两边的球后增加了,sleep,语句,为了避免死锁,需要修改游戏规则,使每个人都只能先抢到两侧中号比较小的球,才能拿另一只球,这样就不会再出现死锁现象,8.2.2,死锁问题,(,续,),例,8_11,运行结果,线程的生命周期,62,8.2.3,控制线程的生命,结束线程的生命,用,stop,方法可以结束线程的生命,但如果一个线程正在操作共享数据段,操作过程没有完成就用,stop,结束的话,将会导致数据的不完整,因此并不提倡使用此方法,通常,可通过控制,run,方法中循环条件的方式来结束一个线程,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 小学资料


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

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


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