1、8.1线程的基本概念线程的基本概念n什么是线程?什么是线程?n线程就是程序中单独顺序的流控制。线程本身线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。不能运行,它只能用于程序中。n线程线程(thread)就是进程中的一个执行线索。就是进程中的一个执行线索。Java虚拟机虚拟机允许进程中同时执行多个线程。每个线程都有一个优允许进程中同时执行多个线程。每个线程都有一个优先级。具有较高优先级的线程先执行。先级。具有较高优先级的线程先执行。n线程是操作系统分配线程是操作系统分配 CPU 时间的基本实体。每一时间的基本实体。每一个应用程序至少有一个线程,也可以拥有多个线程。个应用程序
2、至少有一个线程,也可以拥有多个线程。线程是程序中的代码流。多个线程可以同时运行并能线程是程序中的代码流。多个线程可以同时运行并能共享资源。共享资源。n线程与进程不同,每个进程都需要操作系统为其线程与进程不同,每个进程都需要操作系统为其分配独立的地址空间,而同一进程中的各个线程是在分配独立的地址空间,而同一进程中的各个线程是在同一块地址空间中工作。同一块地址空间中工作。n 在在 Java 程序中,一些动态效果程序中,一些动态效果(如动画的实现、如动画的实现、动态的字幕等动态的字幕等)常利用多线程技术来实现。常利用多线程技术来实现。n什么是多线程?什么是多线程?n多线程则指的是在单个程序中可以同时
3、运行多多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务个不同的线程执行不同的任务.8.1.1线程的生命周期线程的生命周期线程的生命周期:线程的生命周期:一个线程从创建到死一个线程从创建到死亡的过程。亡的过程。线程的生命周期可分为五个状态:线程的生命周期可分为五个状态:1.1.创建状态创建状态 2.2.就绪状态就绪状态3.3.运行状态运行状态 4.4.阻塞状态阻塞状态 5.5.死亡状态死亡状态5线程的生命周期线程的生命周期线程的状态转换图:线程的状态转换图:6线程的生命周期线程的生命周期1.创建状态 当用当用new操作符创建一个新的线程对象时,操作符创建一个新的线程对象时,该线
4、程处于创建状态。该线程处于创建状态。处于创建状态的线程只是一个空的线程对象,处于创建状态的线程只是一个空的线程对象,系统不为它分配资源系统不为它分配资源此时只能调用此时只能调用start方法启动该线程,调用其方法启动该线程,调用其它任何方法都会产生线程非法状态异常。它任何方法都会产生线程非法状态异常。7线程的生命周期线程的生命周期2.就绪状态就绪状态 执行线程的执行线程的start()方法将为线程分配必须的方法将为线程分配必须的系统资源,安排其运行,并调用线程体系统资源,安排其运行,并调用线程体run()方法,这样就使得该线程处于可运行方法,这样就使得该线程处于可运行(Runnable)状态。
5、状态。这一状态并不是运行中状态(这一状态并不是运行中状态(Running),因,因为线程也许实际上并未真正运行。为线程也许实际上并未真正运行。8线程的生命周期线程的生命周期3.3.运行状态运行状态 是某个就绪状态的线程获得是某个就绪状态的线程获得CPUCPU资源资源,正在正在运行运行,如果有更高优先级的线程进入就绪状态如果有更高优先级的线程进入就绪状态,则则该线程就被迫放弃对该线程就被迫放弃对CPUCPU的控制进入就绪状的控制进入就绪状态态,可使用可使用yield()yield()方法主动放弃方法主动放弃CPU,CPU,也可也可能由于执行结束或执行能由于执行结束或执行stop()stop()方
6、法进入死方法进入死亡状态亡状态.9线程的生命周期线程的生命周期4.4.阻塞状态阻塞状态 当发生下列事件时,处于运行状态的线程会转入当发生下列事件时,处于运行状态的线程会转入到阻塞状态。到阻塞状态。调用了调用了sleepsleep()方法;()方法;线程调用线程调用waitwait方法所等待的特定条件的满足方法所等待的特定条件的满足 线程输入线程输入/输出阻塞输出阻塞 10线程的生命周期线程的生命周期从阻塞状态恢复到就绪状态三种途径:从阻塞状态恢复到就绪状态三种途径:处于睡眠状态的线程在指定的时间过去后处于睡眠状态的线程在指定的时间过去后 如果线程在等待某一条件,另一个对象必须通过如果线程在等待
7、某一条件,另一个对象必须通过notify()或或notifyAll()方法通知等待进程条件的改变方法通知等待进程条件的改变 如果线程是因为输入如果线程是因为输入/输出阻塞,输入输出阻塞,输入/输出完成输出完成 118.1.28.1.2线程的优先级线程的优先级1.线程的优先级及其设置线程的优先级及其设置 设置优先级是为了在多线程环境中便于系统对设置优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行线程的调度,优先级高的线程将优先执行 一个线程的优先级设置遵从以下原则:一个线程的优先级设置遵从以下原则:线程创建时,子进程继承父进程的优先级线程创建时,子进程继承父进程的优先级线
8、程创建后,可通过调用线程创建后,可通过调用setPriority()方法改变优先方法改变优先级。级。线程的优先级是线程的优先级是1-10之间的正整数。之间的正整数。1-MIN_PRIORITY,10 MAX_PRIORITY5-NORM_PRIORITY12线程的优先级线程的优先级2.线程的调度策略线程的调度策略 线程调度器选择优先级最高的线程运行。但是,如果线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行发生以下情况,就会终止线程的运行线程体中调用了线程体中调用了yield()方法,让出了对方法,让出了对CPU的占用权的占用权线程体中调用了线程体中调用了slee
9、p()方法方法,使线程进入睡眠状态使线程进入睡眠状态线程由于线程由于I/O操作而受阻塞操作而受阻塞另一个更高优先级的线程出现。另一个更高优先级的线程出现。在支持时间片的系统中,该线程的时间片用完。在支持时间片的系统中,该线程的时间片用完。13线程的生命周期线程的生命周期5.死亡状态死亡状态当线程的当线程的run方法执行结束后,该线程自然方法执行结束后,该线程自然死亡。死亡。148.28.2线程的实现线程的实现在在Java中通过中通过run方法为线程指明要完成的任务,方法为线程指明要完成的任务,有两种技术来为线程提供有两种技术来为线程提供run方法。方法。1.继承继承Thread类并重载类并重载
10、run方法。方法。2.通过定义实现通过定义实现Runnable接口的类进而实现接口的类进而实现run方方法。法。15线程的实现线程的实现1.继承继承Thread类并重载类并重载run方法。方法。Thread类:是专门用来创建线程和对线程进类:是专门用来创建线程和对线程进行操作的类。行操作的类。Thread中定义了许多方法对线中定义了许多方法对线程进行操作。程进行操作。Thread类在缺省情况下类在缺省情况下run方法是空的。可以方法是空的。可以通过继承通过继承Thread类并重写类并重写Thread类的类的run方方法实现用户线程。法实现用户线程。16线程的实现线程的实现1.继承继承Threa
11、d类并重载类并重载run方法。方法。总体结构如下:总体结构如下:public class MyThread extends Thread public void run()MyThread t=new MyThread();t.start();17线程的实现线程的实现2.执行执行Runnable接口的类实现接口的类实现run方法。方法。通过建立一个实现了通过建立一个实现了Runnable接口的对象,并以接口的对象,并以它作为线程的目标对象来创建一个线程。它作为线程的目标对象来创建一个线程。Runnable接口:定义了一个抽象方法接口:定义了一个抽象方法run()。定义。定义如下:如下:publ
12、ic interface java.lang.Runnablepublic abstract void run();18线程的实现线程的实现2.执行执行Runnable接口的类实现接口的类实现run方法。方法。创建的总体框架如下:创建的总体框架如下:class MyRunner implements Runnable public void run()MyRunner r=new MyRunner();Thread t=new Thread(r);19线程的实现线程的实现总结:1.两种方法均需执行线程的两种方法均需执行线程的start方法为线程分方法为线程分配必须的系统资源、调度线程运行并执行
13、配必须的系统资源、调度线程运行并执行线程的线程的run方法。方法。2.在具体应用中,采用哪种方法来构造线程在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来承了另一个类时,就应该用第二种方法来构造,即实现构造,即实现Runnable接口。接口。3.线程的消亡不能通过调用一个线程的消亡不能通过调用一个stop()命令。命令。而是让而是让run()方法自然结束。方法自然结束。208.38.3多线程的互斥多线程的互斥1.为什么要引入互斥机制为什么要引入互斥机制在多线程环境中,可能会有两个甚至更多的线在多线程
14、环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。潜在资源冲突进行预防。解决方法:在线程使用一个资源时为其加锁即解决方法:在线程使用一个资源时为其加锁即可。访问资源的第一个线程为其加上锁以后,可。访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁。其他线程便不能再使用那个资源,除非被解锁。21多线程的互斥多线程的互斥2.怎样实现互斥怎样实现互斥对于访问某个关键共享资源的所有方法,都必对于访问某个关键共享资源的所有方法,都必须把它们设为须把它们设为synchronized例如:例如:sy
15、nchronized void f()/*.*/synchronized void g()/*.*/如果想保护某些资源不被多个线程同时访问,如果想保护某些资源不被多个线程同时访问,可以强制通过可以强制通过synchronized方法访问那些资源。方法访问那些资源。调用调用synchronized方法时,对象就会被锁定。方法时,对象就会被锁定。22多线程的互斥多线程的互斥public class MyStack int idx=0;char data=new char 6;public synchronized void push(char c)data idx=c;idx+;public sy
16、nchronized char pop()idx-;return data idx;23多线程的互斥多线程的互斥说明:当当synchronized方法执行完或发生异常时,方法执行完或发生异常时,会自动释放锁。会自动释放锁。被被synchronized保护的数据应该是私有保护的数据应该是私有(private)的。)的。24多线程的同步多线程的同步怎样实现同步怎样实现同步线程间的相互作用:线程间的相互作用:wait and notify25多线程的同步多线程的同步wait()()和和 notify()()方法:两个方法配方法:两个方法配套使用,套使用,wait()使得线程进入阻塞()使得线程进入阻
17、塞状态,它有两种形式,一种允许指定状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的另一种没有参数,前者当对应的 notify()被调用或者超出指定时间时线程()被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对重新进入可执行状态,后者则必须对应的应的 notify()()被调用。被调用。26多线程的同步多线程的同步2.怎样实现同步怎样实现同步 具有具有wait()和和notify()的线程状态图:的线程状态图:27多线程的同步多线程的同步 除了除了 notify(),还有一个方法(),还有一个方法 notifyAll()()也可起到类似作用,唯一的区别在于,调用也可起到类似作用,唯一的区别在于,调用 notifyAll()方法将把因调用该对象的()方法将把因调用该对象的wait()()方法而阻塞的所有线程一次性全部解除阻塞。方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可当然,只有获得锁的那一个线程才能进入可执行状态。执行状态。