《java程序设计第8章.ppt》由会员分享,可在线阅读,更多相关《java程序设计第8章.ppt(74页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第八章第八章 线程线程JAVA语言程序设计1目录目录l多线程编程基础多线程编程基础l线程的生命周期线程的生命周期l线程的优先级线程的优先级l本章小结本章小结28.1 多线程编程基础多线程编程基础l本节内容本节内容线程的概念Thread类Runnable接口线程间的数据共享多线程的同步控制线程之间的通信后台线程38.1.1 线程的概念线程的概念l进程和线程的区别进程和线程的区别l进程进程一个独立程序的每一次运行称为一个进程,例如l用字处理软件编辑文稿时,同时打开mp3播放程序听音乐,这两个独立的程序在同时运行,称为两个进程设置一个进程要占用相当一部分处理器时间和内存资源大多数操作系统不允许进程访
2、问其他进程的内存空间,进程间的通信很不方便,编程模型比较复杂 多线程编程基础4l线程线程一个程序中多段代码同时并发执行,称为多线程通过多线程,一个进程表面上看同时可以执行一个以上的任务并发创建线程比创建进程开销要小得多,线程之间的协作和数据交换也比较容易Java是第一个支持内置线程操作的主流编程语言多数程序设计语言支持多线程要借助于操作系统“原语(primitives)”8.1.1 线程的概念线程的概念(续续)多线程编程基础58.1.2 Thread类类lThread类类在Java程序中创建多线程的方法之一是继承Thread类 封装了Java程序中一个线程对象需要拥有的属性和方法从Thread
3、类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread类的run方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程将自动进入run方法。原线程将同时继续往下执行Thread类直接继承了Object类,并实现了Runnable接口。它位于java.lang包中,因而程序开头不用import任何包就可直接使用多线程编程基础68.1.2 Thread类类(续续)例例8_1l在新线程中完成计算某个整数的阶乘在新线程中完成计算某个整数的阶乘public class Ex8_1 public static void m
4、ain(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;多线程编程基础7public void run()int i=num;int result=1;
5、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);l运行结果运行结果main thread startsmain thread endsnew thread startedThe factorial of 10 is 3628800new thread ends8.1.2 Thread类类(续续)例例8_1运行结果运行结果多线程编程基
6、础8l结果说明结果说明main线程已经执行完后,新线程才执行完main函数调用thread.start()方法启动新线程后并不等待其run方法返回就继续运行,thread.run函数在一边独自运行,不影响原来的main函数的运行l源程序修改源程序修改如果启动新线程后希望主线程多持续一会再结束,可在start语句后加上让当前线程(这里当然是main)休息1毫秒的语句:try Thread.sleep(1);catch(Exception e);8.1.2 Thread类类(续续)例例8_1修改修改多线程编程基础9l修改后运行结果修改后运行结果main thread startsnew threa
7、d staredThe factorial of 10 is 3628800new thread endsmain thread endsl运行结果说明运行结果说明新线程结束后main线程才结束8.1.2 Thread类类(续续)例例8_1修改后运行结果修改后运行结果多线程编程基础108.1.2 Thread类类(续续)常用常用API函数函数名称说明public Thread()构造一个新的线程对象,默认名为Thread-n,n是从0开始递增的整数public Thread(Runnable target)构造一个新的线程对象,以一个实现Runnable接口的类的对象为参数。默认名为Threa
8、d-n,n是从0开始递增的整数public Thread(String name)构造一个新的线程对象,并同时指定线程名public static Thread currentThread()返回当前正在运行的线程对象public static void yield()使当前线程对象暂停,允许别的线程开始运行public static void sleep(long millis)使当前线程暂停运行指定毫秒数,但此线程并不失去已获得的锁旗标。多线程编程基础11public void start()启动线程,JVM将调用此线程的run方法,结果是将同时运行两个线程,当前线程和执行run方法的线程
9、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函数函数多线程编程
10、基础12public 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()测试线程是否处于活动状态,如果线程被启动并且没有死
11、亡则返回true8.1.2 Thread类类(续续)常用常用API函数函数多线程编程基础13l创建创建3个新线程,每个线程睡眠一段时间(个新线程,每个线程睡眠一段时间(06秒),然秒),然后结束后结束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);(Star
12、ting threads);thread1.start();/启动线程1 thread2.start();/启动线程2 thread3.start();/启动线程3 (Threads started,main endsn);8.1.2 Thread类类(续续)例例8_2多线程编程基础14class TestThread extends Thread private int sleepTime;public TestThread(String name)super(name);sleepTime=(int)(Math.random()*6000);public void run()try (ge
13、tName()+going to sleep for +sleepTime);Thread.sleep(sleepTime);/线程休眠 catch(InterruptedException exception);(getName()+finished 8.1.2 Thread类类(续续)例例8_2多线程编程基础15l运行结果运行结果Starting threadsThreads started,main endsthread1 going to sleep for 3519thread2 going to sleep for 1689thread3 going to sleep for 55
14、65thread2 finishedthread1 finishedthread3 finishedl说明说明由于线程3休眠时间最长,所以最后结束,线程2休眠时间最短,所以最先结束每次运行,都会产生不同的随机休眠时间,所以结果都不相同8.1.2 Thread类类(续续)例例8_2运行结果运行结果多线程编程基础168.1.3 Runnable接口接口lRunnable接口接口Java多线程机制的一个重要部分,实际上它只有一个run()方法Thread类实现了Runnable接口,相对于Thread类,它更适合于多个线程处理同一资源实现Runnable接口的类的对象可以用来创建线程,这时start
15、方法启动此线程就会在此线程上运行run()方法在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线程多线程编程基础17l使用使用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.pri
16、ntln(new thread started,main thread ends );8.1.3 Runnable接口接口(续续)例例8_3多线程编程基础18class 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+resul
17、t);System.out.println(new thread ends);8.1.3 Runnable接口接口(续续)例例8_3多线程编程基础19l使用使用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();(Starting threads);new Thread(th
18、read1,Thread1).start();new Thread(thread2,Thread2).start();new Thread(thread3,Thread3).start();(Threads started,main endsn);8.1.3 Runnable接口接口(续续)例例8_4多线程编程基础20class TestThread implements Runnable private int sleepTime;public TestThread()sleepTime=(int)(Math.random()*6000);public void run()try (Thre
19、ad.currentThread().getName()+going to sleep for +sleepTime);Thread.sleep(sleepTime);catch(InterruptedException exception);(Thread.currentThread().getName()+finished);8.1.3 Runnable接口接口(续续)例例8_4多线程编程基础218.1.4 线程间的数据共享线程间的数据共享l代码共享代码共享多个线程的执行代码来自同一个类的run方法时,即称它们共享相同的代码l数据共享数据共享当共享访问相同的对象时,即它们共享相同的数据使用
20、Runnable接口可以轻松实现多个线程共享相同数据,只要用同一个实现了Runnable接口的实例作为参数创建多个线程就可以了多线程编程基础22l修改例修改例8_4,只用一个,只用一个Runnable类型的对象为参类型的对象为参数创建数创建3个新线程。个新线程。public class Ex8_5 public static void main(String args)TestThread threadobj=new TestThread();(Starting threads);new Thread(threadobj,Thread1).start();new Thread(threadob
21、j,Thread2).start();new Thread(threadobj,Thread3).start();(Threads started,main endsn);8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_5多线程编程基础23 class TestThread implements Runnable private int sleepTime;public TestThread()sleepTime=(int)(Math.random()*6000);public void run()try (Thread.currentThread().getName()+goin
22、g to sleep for +sleepTime);Thread.sleep(sleepTime);catch(InterruptedException exception);(Thread.currentThread().getName()+finished);8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_5多线程编程基础24l运行结果运行结果Starting threadsThread1 going to sleep for 966Thread2 going to sleep for 966Threads started,main endsThread3 going to
23、sleep for 966Thread1 finishedThread2 finishedThread3 finishedl说明说明因为是用一个Runnable类型对象创建的3个新线程,这三个线程就共享了这个对象的私有成员sleepTime,在本次运行中,三个线程都休眠了966毫秒8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_5运行结果运行结果多线程编程基础258.1.4 线程间的资源共享线程间的资源共享(续续)l独立的同时运行的线程有时需要共享一些数据并且独立的同时运行的线程有时需要共享一些数据并且考虑到彼此的状态和动作考虑到彼此的状态和动作例如生产/消费问题:生产线程产生数据
24、流,然后这些数据流再被消费线程消费假设一个Java应用程序,其中有一个线程负责往文件写数据,另一个线程从同一个文件中往出都数据,因为涉及到同一个资源,这里是同一个文件,这两个线程必须保证某种方式的同步多线程编程基础26l用三个线程模拟三个售票口,总共出售用三个线程模拟三个售票口,总共出售200张票张票用3个线程模仿3个售票口的售票行为这3个线程应该共享200张票的数据public class Ex8_6public static void main(String args)SellTickets t=new SellTickets();new Thread(t).start();new Thr
25、ead(t).start();new Thread(t).start();多线程编程基础8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_627class SellTickets implements Runnableprivate int tickets=200;public void run()while(tickets0)(Thread.currentThread().getName()+is selling ticket+tickets-);8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_6多线程编程基础28l运行结果选最后几行如下运行结果选最后几行如下Threa
26、d-2 is selling ticket 6Thread-1 is selling ticket 5Thread-0 is selling ticket 4Thread-2 is selling ticket 3Thread-1 is selling ticket 2Thread-0 is selling ticket 1l说明说明在这个例子中,创建了3个线程,每个线程调用的是同一个SellTickets对象中的run()方法,访问的是同一个对象中的变量(tickets)如果是通过创建Thread类的子类来模拟售票过程,再创建3个新线程,则每个线程都会有各自的方法和变量,虽然方法是相同的,但
27、变量却是各有200张票,因而结果将会是各卖出200张票,和原意就不符了8.1.4 线程间的数据共享线程间的数据共享(续续)例例8_6运行结果运行结果多线程编程基础298.1.5 多线程的同步控制多线程的同步控制l有时线程之间彼此不独立、需要同步有时线程之间彼此不独立、需要同步线程间的互斥l同时运行的几个线程需要共享一个(些)数据l一个线程对共享的数据进行操作时,不允许其他线程打断它,否则会破坏数据的完整性。即被多个线程共享的数据,在某一时刻只允许一个线程对其进行操作“生产者/消费者”问题l生产者产生数据,消费者消费数据,具体来说,假设有一个Java应用程序,其中有一个线程负责往数据区写数据,另
28、一个线程从同一数据区中读数据,两个线程可以并行执行(类似于流水线上的两道工序)l如果数据区已满,生产者要等消费者取走一些数据后才能再放;而当数据区没有数据时,消费者要等生产者放入一些数据后再取多线程编程基础30l用两个线程模拟存票、售票过程用两个线程模拟存票、售票过程 假定开始售票处并没有票,一个线程往里存票,另外一个线程则往出卖票我们新建一个票类对象,让存票和售票线程都访问它。本例采用两个线程共享同一个数据对象来实现对同一份数据的操作public class Ex8_7 public static void main(String args)Tickets t=new Tickets(10)
29、;new Consumer(t).start();new Producer(t).start();8.1.5 多线程的同步控制多线程的同步控制(续续)例例8_7多线程编程基础31class Tickets int number=0;/票号int size;/总票数boolean available=false;/表示目前是否有票可售public Tickets(int size)/构造函数,传入总票数参数this.size=size;8.1.5 多线程的同步控制多线程的同步控制(续续)例例8_7多线程编程基础32class Producer extends ThreadTickets t=nu
30、ll;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多线程编程基础33class Consumer extends Thread /售票线程售票线程Tickets t=null;int i=0;public Consumer(Tickets t)this.t=t;public void run(
31、)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多线程编程基础34l运行结果运行结果Producer puts ticket 1Producer puts ticket 2Producer puts ticket 3Producer puts ticket 4Producer puts ticket 5Producer puts tick
32、et 6Producer puts ticket 7Producer puts ticket 8Consumer buys ticket 1Consumer buys ticket 2Consumer buys ticket 3Consumer buys ticket 4Consumer buys ticket 5Consumer buys ticket 6Consumer buys ticket 7Consumer buys ticket 8Producer puts ticket 9Producer puts ticket 10Consumer buys ticket 9Consumer
33、buys ticket 10.l通过让两个线程操纵同一个票类对象,实现了数据共享的目的通过让两个线程操纵同一个票类对象,实现了数据共享的目的8.1.5 多线程的同步控制多线程的同步控制(续续)例例8_7运行结果运行结果多线程编程基础35l设想一下,假如售票线程运行到设想一下,假如售票线程运行到t.available=false之前,之前,CPU切换切换到存票线程,存票线程将到存票线程,存票线程将available置为置为true,并直到整个存票线程,并直到整个存票线程结束。再次切换到售票线程后,售票线程执行结束。再次切换到售票线程后,售票线程执行t.available=false。此时售票号小
34、于存票数,且存票线程已经结束不再能将此时售票号小于存票数,且存票线程已经结束不再能将t.available置为置为true,则售票线程陷入了死循环,则售票线程陷入了死循环l如果我们在如果我们在t.available=false之前加上之前加上sleep语句,让售票线程多停语句,让售票线程多停留一会,则可以更加清楚地看到这个问题留一会,则可以更加清楚地看到这个问题if(i=t.number)try Thread.sleep(1);catch(InterruptedException exception);t.available=false;8.1.5 多线程的同步控制多线程的同步控制(续续)例例
35、8_7修改修改多线程编程基础36l修改后运行结果修改后运行结果Producer puts ticket 1Producer puts ticket 2Producer puts ticket 3Producer puts ticket 4Producer puts ticket 5Producer puts ticket 6Producer puts ticket 7Producer puts ticket 8Consumer buys ticket 1Consumer buys ticket 2Consumer buys ticket 3Consumer buys ticket 4Consu
36、mer buys ticket 5Consumer buys ticket 6Consumer buys ticket 7Consumer buys ticket 8Producer puts ticket 9Producer puts ticket 108.1.5 多线程的同步控制多线程的同步控制(续续)例例8_7修改后运行结果修改后运行结果多线程编程基础37l如何避免上面这种意外,让我们的程序是如何避免上面这种意外,让我们的程序是“线程安线程安全全”的呢?的呢?解决线程的同步/互斥问题存票线程和售票线程应保持互斥关系。即售票线程执行时不进入存票线程、存票线程执行时不进入售票线程lJava
37、使用的同步机制是监视器使用的同步机制是监视器每个对象都只有一个“锁旗标”与之相连,利用多线程对其的争夺可实现线程间的互斥操作当线程A获得了一个对象的锁旗标后,线程B必须等待线程A完成规定的操作、并释放出锁旗标后,才能获得该对象的锁旗标,并执行线程B中的操作8.1.5 多线程的同步控制多线程的同步控制(续续)解决例解决例8_7的问题的问题多线程编程基础38l线程同步的概念,包括互斥和协作线程同步的概念,包括互斥和协作互斥:许多线程在同一个共享数据上操作而互不干扰,同一时刻只能有一个线程访问该共享数据。因此有些方法或程序段在同一时刻只能被一个线程执行,称之为监视区协作:多个线程可以有条件地同时操作
38、共享数据。执行监视区代码的线程在条件满足的情况下可以允许其它线程进入监视区8.1.5 多线程的同步控制多线程的同步控制(续续)线程同步线程同步(Synchronization)多线程编程基础39lsynchronized 线程同步关键字线程同步关键字用于指定需要同步的代码段或方法,也就是监视区可实现与一个锁旗标的交互。例如:lsynchronized(对象)代码段 synchronized的功能是:首先判断对象的锁旗标是否在,如果在就获得锁旗标,然后就可以执行紧随其后的代码段;如果对象的锁旗标不在(已被其他线程拿走),就进入等待状态,直到获得锁旗标当被synchronized限定的代码段执行完
39、,就释放锁旗标8.1.5 多线程的同步控制多线程的同步控制(续续)synchronized关键字关键字多线程编程基础40l将需要互斥的语句段放入将需要互斥的语句段放入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(P
40、roducer puts ticket+(+t.number);t.available=true;/释放对象t的锁旗标System.out.println(Producer ends!);8.1.5 多线程的同步控制多线程的同步控制(续续)synchronized关键字关键字多线程编程基础41class 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.ava
41、ilable=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关键字关键字多线程编程基础42l说明说明存票程序段和售票程序段为获得同一对象的锁旗标而实现互斥操作当线程执行到synchronized的时候,检查传入的实参对象,
42、并申请得到该对象的锁旗标。如果得不到,那么线程就被放到一个与该对象锁旗标相对应的等待线程池中。直到该对象的锁旗标被归还,池中的等待线程才能重新去获得锁旗标,然后继续执行下去除了可以对指定的代码段进行同步控制之外,还可以定义整个方法在同步控制下执行,只要在方法定义前加上synchronized关键字即可8.1.5 多线程的同步控制多线程的同步控制(续续)synchronized关键字关键字多线程编程基础43l实现例实现例8_7功能。将互斥方法放在共享的资源类功能。将互斥方法放在共享的资源类Tickets中中class Tickets int size;/票总数int number=0;/存票序号
43、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多线程编
44、程基础48class 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多线程编程基础49l运行
45、结果运行结果Producer puts ticket 1Consumer buys ticket 1Producer puts ticket 2Consumer buys ticket 2Producer puts ticket 3Consumer buys ticket 3Producer puts ticket 4Consumer buys ticket 4Producer puts ticket 5Consumer buys ticket 5Producer puts ticket 6Consumer buys ticket 6Producer puts ticket 7Consumer
46、 buys ticket 7Producer puts ticket 8Consumer buys ticket 8Producer puts ticket 9Consumer buys ticket 9Producer puts ticket 10Consumer buys ticket 108.1.6 线程之间的通信线程之间的通信(续续)例例8_9运行结果运行结果多线程编程基础50l程序说明程序说明当Consumer线程售出票后,available值变为false,当Producer线程放入票后,available值变为true只有available为true时,Consumer线程才能售
47、票,否则就必须等待Producer线程放入新的票后的通知只有available为false时,Producer线程才能放票,否则必须等待Consumer线程售出票后的通知可见通过线程间的通信实现了我们的要求8.1.6 线程之间的通信线程之间的通信(续续)例例8_9说明说明多线程编程基础518.1.7 后台线程后台线程l后台线程后台线程也叫守护线程,通常是为了辅助其它线程而运行的线程它不妨碍程序终止一个进程中只要还有一个前台线程在运行,这个进程就不会结束;如果一个进程中的所有前台线程都已经结束,那么无论是否还有未结束的后台线程,这个进程都会结束“垃圾回收”便是一个后台线程如果对某个线程对象在启动
48、(调用start方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程多线程编程基础52l创建一个无限循环的后台线程,验证主线程结束后,程序即创建一个无限循环的后台线程,验证主线程结束后,程序即结束结束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)l运行程序,则发现整个程序在
49、主线程结束时就随之中止运行运行程序,则发现整个程序在主线程结束时就随之中止运行了,如果注释掉了,如果注释掉t.setDaemon(true)语句,则程序永远不会语句,则程序永远不会结束结束8.1.7 后台线程后台线程(续续)例例8_10多线程编程基础538.2 线程的生命周期线程的生命周期l线程的生命周期线程的生命周期线程从产生到消亡的过程 一个线程在任何时刻都处于某种线程状态(thread state)548.2.1 线程的几种基本状态线程的几种基本状态l线程生命周期状态图线程生命周期状态图线程的生命周期55l诞生状态诞生状态线程刚刚被创建l就绪状态就绪状态线程的 start 方法已被执行线
50、程已准备好运行l运行状态运行状态处理机分配给了线程,线程正在运行l阻塞状态(阻塞状态(Blocked)在线程发出输入/输出请求且必须等待其返回遇到用synchronized标记的方法而未获得其监视器暂时不能进入执行时l休眠状态(休眠状态(Sleeping)执行sleep方法而进入休眠l死亡状态死亡状态线程已完成或退出8.2.1 线程的几种基本状态线程的几种基本状态(续续)线程的生命周期568.2.2 死锁问题死锁问题l死锁死锁线程在运行过程中,其中某个步骤往往需要满足一些条件才能继续进行下去,如果这个条件不能满足,线程将在这个步骤上出现阻塞线程A可能会陷于对线程B的等待,而线程B同样陷于对线程