1、第十三章第十三章 并发并发1线程的概念线程的概念线程的创建线程的创建线程调度与线程控制线程调度与线程控制线程同步线程同步线程状态与生命周期线程状态与生命周期2?什么是线程什么是线程?多线程是实现并发的一种有效手段。多线程是实现并发的一种有效手段。?一个进程可以通过运行多个线程来并发地执行多项任务。一个进程可以通过运行多个线程来并发地执行多项任务。?多个线程如何调度执行由系统来实现。多个线程如何调度执行由系统来实现。3什么是线程什么是线程?线程是程序中的单个执行流,多线程是一个程序线程是程序中的单个执行流,多线程是一个程序 中包含中包含的多个同时运行的执行流。的多个同时运行的执行流。4线程与进程
2、线程与进程?进程:内核级的实体。包含虚存映象、文件指示符,进程:内核级的实体。包含虚存映象、文件指示符,用户用户ID等。这些结构都在内核空间中,用户程序等。这些结构都在内核空间中,用户程序只有通过系统调用才能访问与改变。只有通过系统调用才能访问与改变。?线程:用户级的实体。线程结构驻留在用户空间中,线程:用户级的实体。线程结构驻留在用户空间中,能够被普通的用户级函数组成的线程库直接访问。能够被普通的用户级函数组成的线程库直接访问。寄存器(栈指针,程序计数器)是线程专有的成分。寄存器(栈指针,程序计数器)是线程专有的成分。?一个进程中的所有线程共享该进程的状态。一个进程中的所有线程共享该进程的状
3、态。5Java中的线程中的线程?Java 中线程被认为是一个中线程被认为是一个CPU、程序代码、和数据、程序代码、和数据的封装体。的封装体。CPU?一个虚拟的一个虚拟的CPU,?该该CPU执行的代码:执行的代码:CodeData代码与数据是相互独立的,代代码与数据是相互独立的,代码可以与其它线程共享。码可以与其它线程共享。?代码所操作的数据:数据也可以代码所操作的数据:数据也可以被多个线程共享。被多个线程共享。6线程的构造线程的构造?Java.lang.Thread类使用户可以创建和控制自己的线程。类使用户可以创建和控制自己的线程。?在在Java中,虚拟中,虚拟CPU是自动封装进是自动封装进T
4、hread类的实例中,类的实例中,而而Code和和Data要通过一个对象传给要通过一个对象传给Thread类的构造函数。类的构造函数。7线程的创建线程的创建?线程的线程的Code和和Data构成线程体。线程体决定了线程的行为。构成线程体。线程体决定了线程的行为。?Java中线程体由中线程体由Thread类的类的 run()方法定义,该方法方法定义,该方法定义了线程的具体行为并指定了线程要操作的数据。定义了线程的具体行为并指定了线程要操作的数据。?有两种方式进行有两种方式进行run()方法的定义:方法的定义:?实现实现 Runnable 接口接口?继承继承Thread 类类8通过实现通过实现Ru
5、nnable 接口创建线程接口创建线程?Runnable 接口只提供了一个接口只提供了一个public void run()方法。方法。?定义一个类实现定义一个类实现Runnable接口。接口。?将该类的实例作为参数传给将该类的实例作为参数传给Thread类的一个构造函数,类的一个构造函数,从而创建一个线程。从而创建一个线程。9线程创建示例线程创建示例Public class ThreadTestpublic static void main(String args)Xyz r=new Xyz();Thread t=new Thread(r);t.start();Class Xyz imple
6、ments Runnableint I;public void run()while(true)System.out.println(“Hello”+I+);if(I=5)break;10线程创建示例线程创建示例线程线程 tCPU一个线程就是一个线程就是Thread类的一个实例。类的一个实例。线程是从一个传递给线程的线程是从一个传递给线程的Runnable实例的实例的run()方法开始执行。方法开始执行。线程所操作的数据是来自于该线程所操作的数据是来自于该Runnable 类的实例。类的实例。CodeDataXyz类类Xyz的实例的实例 r 11通过继承通过继承Thread类创建线程类创建线程
7、?Thread 类本身实现了类本身实现了Runnable接口。接口。?通过继承通过继承Thread类,重写其中的类,重写其中的run()方法定义线程体。方法定义线程体。?创建该子类的对象创建线程。创建该子类的对象创建线程。Public class Counter extends Threadpublic void run()创建与运行线程:创建与运行线程:Counter ThreadCounter=new Counter();ThreadCounter.Start();12线程两种创建方法比较线程两种创建方法比较?实现实现Runnable接口的优势:接口的优势:?符合符合OO设计的思想。设计的
8、思想。?便于用便于用extends继承其它类。继承其它类。?采用继承采用继承Thread类方法的优点:程序代码更简单。类方法的优点:程序代码更简单。?提倡采用第一种方式。提倡采用第一种方式。13线程的运行线程的运行?新创建的线程不会自动运行。必须调用线程的新创建的线程不会自动运行。必须调用线程的start()方法方法,如:如:t.start()?该方法的调用把嵌入在线程中的虚拟该方法的调用把嵌入在线程中的虚拟CPU置为可运行置为可运行(Runnable)状态。状态。?Runnable状态意味着该线程可以参加调度,被状态意味着该线程可以参加调度,被JVM运行,运行,并不意味着线程会立即执行。并不
9、意味着线程会立即执行。14线程调度与线程控制线程调度与线程控制?线程调度策略线程调度策略?线程的基本控制线程的基本控制15线程调度策略线程调度策略?Java中线程调度采用抢先式调度方法。中线程调度采用抢先式调度方法。?抢先式调度模式:抢先式调度模式:许多线程可能是可运行的,但只能有一个线程在运行。许多线程可能是可运行的,但只能有一个线程在运行。该线程将持续运行,直到它自行中止或出现高优先级该线程将持续运行,直到它自行中止或出现高优先级线程成为可运行的,则该低优先级线程被高优先级线程线程成为可运行的,则该低优先级线程被高优先级线程强占运行。强占运行。?线程中止的原因可能有多种,如执行线程中止的原
10、因可能有多种,如执行Thread.sleep()调用,调用,或等待访问共享的资源。或等待访问共享的资源。16线程的优先级线程的优先级?每个线程都有优先级,有缺省值,可用每个线程都有优先级,有缺省值,可用SetPriority()方法改变。方法改变。?每个优先级有一个等待池:每个优先级有一个等待池:t1t2t3.poolpoolpool17线程调度策略线程调度策略?JVM先运行高优先级池中的线程,待该池空后才先运行高优先级池中的线程,待该池空后才考虑低优先级线程。考虑低优先级线程。?如果有高优先级线程成为可运行的,则运行它。如果有高优先级线程成为可运行的,则运行它。?抢先式可能是分时的,即每个池
11、中的线程轮流运行;抢先式可能是分时的,即每个池中的线程轮流运行;也可能不是,即线程逐个运行,由也可能不是,即线程逐个运行,由JVM而定。而定。?线程一般可用线程一般可用sleep()保证给其他线程运行时间。保证给其他线程运行时间。18线程的基本控制线程的基本控制?结束线程结束线程?获取当前线程获取当前线程?测试线程测试线程?sleep()?join()?yield()19结束线程结束线程?线程完成运行并结束后,将不能再运行。线程完成运行并结束后,将不能再运行。?除正常运行结束外,还可用其他方法控制使其停止。除正常运行结束外,还可用其他方法控制使其停止。?用用stop()方法。方法。强行终止线程
12、,容易造成线程的不一致。强行终止线程,容易造成线程的不一致。?使用标志使用标志flag。通过设置通过设置flag 指明指明run()方法应该结束。方法应该结束。20结束线程结束线程class Xyz implements Runnableprivate boolean timeToQuit=false;public void run()while(!timeToQuit)/clean up before run()ends.public void stopRunning()timeToQuit=true;public class ControlThreadpublic void main(St
13、ring args)Runnable r=new Xyz();Thread t=new Thread(r);t.start();r.stopRunning();21获取当前线程获取当前线程Thread 类的静态方法类的静态方法currentThread()返回当前线程。返回当前线程。22测试线程测试线程当线程的状态未知时,用当线程的状态未知时,用isAlive()确定线程是)确定线程是否活着。返回否活着。返回true 意味着线程已经启动,但还没有意味着线程已经启动,但还没有运行结束。运行结束。23Sleep()方法方法?该方法用来使一个线程暂停运行一段固定的时间。该方法用来使一个线程暂停运行一
14、段固定的时间。在线程睡眠时间内,将运行别的线程。在线程睡眠时间内,将运行别的线程。?Sleep()结束后,线程将进入结束后,线程将进入Runnable状态。状态。24join()方法方法t.join()方法使当前的线程等待,直到方法使当前的线程等待,直到 t 结束为止,线程结束为止,线程恢复到恢复到runnable状态。状态。Public void doTask()TimerThread tt=new TimerThread(100);tt.start();/Do stuff in parallel with the other thread for a while/Wait here for
15、 the timer thread to finishtrytt.join();catch(InterruptedException e)/tt came back early/continue in this thread25yield()方法方法?调用该方法将调用该方法将CPU让给具有与当前线程相同优先级让给具有与当前线程相同优先级的线程。的线程。?如果没有同等优先级的线程是如果没有同等优先级的线程是Runnable状态,状态,yield()方法将什么也不做。方法将什么也不做。26线程同步线程同步?线程间同步机制线程间同步机制?线程间的交互线程间的交互wait()和和notify()?不建
16、议使用的一些方法不建议使用的一些方法27多线程并发执行中的问题多线程并发执行中的问题?多个线程相对执行的顺序是不确定的。多个线程相对执行的顺序是不确定的。?线程执行顺序的不确定性会产生执行结果的不确定性。线程执行顺序的不确定性会产生执行结果的不确定性。?在多线程对共享数据在多线程对共享数据 操作时常常会产生这种不确定性。操作时常常会产生这种不确定性。28多线程并发执行中的问题多线程并发执行中的问题一个堆栈类:一个堆栈类:public class MyStackprivate int idx=0;private char data=new char6;public void push(char
17、c)dataidx=c;idx+;/栈顶指针指向下一个空单元栈顶指针指向下一个空单元public char pop()idx-;return dataidx;29多线程并发执行中的问题多线程并发执行中的问题时刻时刻t1t2时刻时刻t3线程线程 a线程线程b堆栈堆栈 s 状态状态|p|q|/调用调用s.Push();idx=2dataidx=r ;|p|q|r|/*线程线程a 被抢占被抢占idx=2idx+;.未执行未执行*/调用调用s.pop().idx-;|p|q|r|.return dataidx;idx=1/恢复运行恢复运行idx+;|p|q|r|!“q”被两次压栈;线程a 压入的数据“
18、r”丢失。idx=230时刻时刻对象锁的概念对象锁的概念?Java中每个对象都带有一个中每个对象都带有一个monitor标志,标志,相当于一个锁。相当于一个锁。?Sychronized关键字用来给对象加上独占的排它锁。关键字用来给对象加上独占的排它锁。31对象锁的操作对象锁的操作Thread before synchronized(this)Public void push(char c)synchronized(this)dataidx=c;idx+;Object thisCode or behaviorData or state32对象锁的操作对象锁的操作Object thisCode o
19、r behaviorData or stateThread after synchronized(this)Public void push(char c)synchronized(this)dataidx=c;idx+;33示例示例将将MyStack 对栈的对栈的push,pop操作都采用这种机制:操作都采用这种机制:public class MyStack public void push(char c)synchronized(this)dataidx=c;idx+;public char pop()synchronized(this)idx-;return dataidx;34多线程对
20、共享数据进行操作多线程对共享数据进行操作时刻时刻t1t2t3时刻时刻t4线程线程 a线程线程b堆栈堆栈 s 状态状态/*调调 用用s.push(),获得获得s的的monitor后运行后运行*/|p|q|r|dataidx=r ;/线程线程a 被抢占被抢占idx=2./*调用调用s.pop(),未获得未获得s的的monitor,.b到到s的的lock pool中等待中等待*/|p|q|r|.idx=2/恢复运行恢复运行idx+;|p|q|r|。/*完成,并交回完成,并交回S的的idx=3monitor*/运行运行pop|p|q|r|得结果得结果ridx=235时刻时刻时刻时刻几点说明几点说明如何
21、返还对象的如何返还对象的monitor?当当synchronized()语句块执行完毕后。语句块执行完毕后。?当在当在synchronized()语句块中出现语句块中出现exception.?当调用该对象的当调用该对象的wait()方法。将该线程放入方法。将该线程放入对象的对象的wait pool中,等待某事件的发生。中,等待某事件的发生。36几点说明几点说明?对共享数据的所有访问都必须使用对共享数据的所有访问都必须使用synchronized.?用用synchronized保护的共享数据必须是私有的,使线程保护的共享数据必须是私有的,使线程不能直接访问这些数据,必须通过对象的方法。不能直接访
22、问这些数据,必须通过对象的方法。?如果一个方法的整体都在如果一个方法的整体都在synchronized块中,则可以把块中,则可以把synchronized关键字放于方法定义的头部:关键字放于方法定义的头部:public synchronized void push(char c)37几点说明几点说明public class Reentrant public class Reentrant public synchronized void a()public synchronized void a()b();b();System.out.println(here I am,in a();Syst
23、em.out.println(here I am,in a();public synchronized void b()public synchronized void b()System.out.println(here I am,in b();System.out.println(here I am,in b();Java运行系统允许已经拥有某个对象所的线程再次获得该运行系统允许已经拥有某个对象所的线程再次获得该对象的锁对象的锁 Java locks are reentrant.38避免死锁避免死锁?死锁是指两个线程同时等待对方持有的锁。死锁是指两个线程同时等待对方持有的锁。?死锁的避免完
24、全由程序控制。死锁的避免完全由程序控制。?可以采用的方法:可以采用的方法:如果要访问多个共享数据对象,则要从全局考虑定义如果要访问多个共享数据对象,则要从全局考虑定义一个获得封锁的顺序,并在整个程序中都遵守这个一个获得封锁的顺序,并在整个程序中都遵守这个顺序。释放锁时,要按加锁的反序释放。顺序。释放锁时,要按加锁的反序释放。39线程间的交互线程间的交互?Wait()和和notify()线程在线程在synchronized块中调用块中调用x.wait()等待共享数据等待共享数据的某种状态。该线程将放入对象的某种状态。该线程将放入对象x的的wait pool,并且将释并且将释放放x的的monito
25、r。线程在改变共享数据的状态后,调用线程在改变共享数据的状态后,调用x.notify(),则对象,则对象的的wait pool中的一个线程将移入中的一个线程将移入lock pool,等待,等待x的的monitor,一旦获得便可以运行。一旦获得便可以运行。?Notifyall()把对象把对象wait pool中的所有线程都移入中的所有线程都移入lock pool。40示例示例-Producer/ConsumerProducer线程:每隔线程:每隔300ms产生一个字母压栈产生一个字母压栈theStack,共共200个。个。Public void run()char c;for(int I=0;I
26、200;I+)c=(char)(math.random()*26+A);theStack.push(c);tryThread.sleep(300);catch(InterruptedException e)41示例示例-Producer/ConsumerConsumer线程:从栈线程:从栈theStack中取中取200个字符,间隔个字符,间隔300ms。Public void run()char c;for(int I=0;I200;I+)c=theStack.pop();tryThread.sleep(300);catch(InterruptedException e)42示例示例-Prod
27、ucer/Consumer堆栈类堆栈类SyncStack:?为了保证共享数据一致性,为了保证共享数据一致性,push()与与pop()定义成定义成synchronized;?为了实现为了实现Producer 与与 Consumer之间的同步,加入之间的同步,加入wait()与与notify()public class SyncStackprivate Vector buffer=new Vector(400,200);public synchronized char pop();public synchronized void push(char c)43示例示例-Producer/Consu
28、merPop()方法:方法:public synchronized char pop()char c;while(buffer.size()=0)trythis.wait();catch(InterruptedException e)c=(Character)buffer.remover(buffer.size()-1).charValue();return c;44示例示例-Producer/ConsumerPush()方法方法:public synchronized void push(char c)this.notify();Character charObj=new Character
29、(c);buffer.addElement(char Obj);45不建议使用的方法不建议使用的方法?Stop()线程强行终止,容易造成数据的不一致。线程强行终止,容易造成数据的不一致。?suspend()和和 resume()使一个线程使一个线程A可以通过调用可以通过调用B.suspend()直接控制直接控制B的运行。的运行。Suspend()方法将不使方法将不使B释放锁。容易发生死锁。建议使用释放锁。容易发生死锁。建议使用wait()和和notify().46线程状态与生命周期线程状态与生命周期NewStart()OtherwiseBlockedSleep()or join()run()e
30、ndswait()notify()Thread joins orinterruptschedulerRunnableyield()RunningDeadSynchronized()Blocked in ObjcetsBlocked inObjectslock poolwait poolInterrupt()47线程状态线程状态?几种基本状态几种基本状态new,Runnable,Running,Dead,Blocked等。等。?线程状态由线程控制方法,如线程状态由线程控制方法,如sleep(),join()或线程同步或线程同步控制方法引起变化。控制方法引起变化。48线程分组(线程分组(Group
31、ing Threads)?线程组:线程组:?每个每个Java线程都是归属于一个线程组。线程组机制把多个线程集合线程都是归属于一个线程组。线程组机制把多个线程集合为一个对象,从而可以实现对这些线程整体操作。为一个对象,从而可以实现对这些线程整体操作。?线程组由线程组由java.lang中的中的ThreadGroup 类实现。类实现。?线程属于某个线程组具有永久性。线程属于某个线程组具有永久性。?缺省线程组缺省线程组?在在Java Application启动时,运行系统创建缺省的线程组,称为启动时,运行系统创建缺省的线程组,称为main。所有未指定组的线程,均属于该组。所有未指定组的线程,均属于该
32、组。?未指定组的线程,将属于其未指定组的线程,将属于其“父线程父线程”所在线程组。所在线程组。?创建属于线程组的线程创建属于线程组的线程public Thread(ThreadGroup public Thread(ThreadGroup groupgroup,Runnable,Runnable runnablerunnable);public Thread(ThreadGroup public Thread(ThreadGroup groupgroup,String,String namename);public Thread(ThreadGroup public Thread(Thread
33、Group groupgroup,Runnable,Runnable runnablerunnable,String,String namename);49ThreadGroup 类类ThreadGroupThreadGroup 类的方法:类的方法:?Collection Management Methods:activeCountactiveCount,listCurrentThreadslistCurrentThreads?Methods that Operate on the Group:getMaxPrioritygetMaxPriority,getNamegetName?Methods that Operate on All Threads within a Group:resumeresume,stopstop,?Access Restriction Methods:checkAccesssuspendsuspend50