第6章java的多线程_第1页
第6章java的多线程_第2页
第6章java的多线程_第3页
第6章java的多线程_第4页
第6章java的多线程_第5页
已阅读5页,还剩93页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第12章

线

程第12章

线

程主要内容线程的概念线程的生命周期Java中多线程的编程继承Thread类与使用Runnable接口Thread类的主要方法线程的同步与死锁第12章

线

程基本概念之一:进程进程是正在运行的一个程序程序:静态对象--进程:动态过程操作系统为每个进程分配一段内存空间,包括:代码、数据以及堆栈等资源多任务的操作系统(OS)中,进程切换对CPU资源消耗较大第12章

线

程基本概念之二:多线程单线程多线程进程传统进程多线程进程第12章

线

程基本概念之二:多线程线程是比进程更小一级的执行单元线程不能独立存在,必须存在于进程中,各线程间共享进程空间的数据线程创建、销毁和切换的负荷远小于进程,又称为轻量级进程(lightweightprocess)。第12章

线

程线程的概念程序是一段静态的代码,它是应用程序执行的蓝本一个进程既包括其所要执行的指令,也包括了执行指令所需的任何系统资源,如CPU、内存空间、I/O端口等,不同进程所占用的系统资源相对独立线程是进程执行过程中产生的多条执行线索,是比进程单位更小的执行单位第12章

线

程线程的结构CPUCodeData虚拟CPU,封装在

java.lang.Thread类中,它控制着整个线程的运行执行的代码,传递给Thread类,由

Thread类控制顺序执行处理的数据,传递给

Thread类,是在代码执行过程中所要处理的数据第12章

线

程线程与进程线程在形式上同进程十分相似—都是用一个顺序执行的语句序列来完成特定的功能不同之处:线程没有入口,也没有出口,因此其自身不能自动运行,而必须栖身于某一进程之中,由进程触发执行在系统资源的使用上,属于同一进程的所有线程共享该进程的系统资源,但是线程之间切换的速度比进程切换要快得多第12章

线

程进程与线程的区别文件输入输出装置各种系统资源数据区段程序区段只有一个地方在执行文件输入输出装置各种系统资源数据区段程序区段同时有数个地方在执行传统的进程多线程的任务第12章

线

程多线程的优势多线程编程简单,效率高。使用多线程可以在线程间直接共享数据和资源,而多进程之间不能做到这一点适合于开发服务程序–如Web服务、聊天服务等适合于开发有多种交互接口的程序–如聊天程序的客户端、网络下载工具适合于有人机交互又有计算量的程序–如字处理程序Word、Excel等第12章

线

程线程的调度调度策略–时间片–抢占式:高优先级的线程抢占CPUJava的调度方法同优先级线程组成先进先出队列,使用时间片策略对高优先级,使用优先调度的抢占式策略12第12章

线

程线程的状态Java的线程是通过Java的软件包java.lang中定义的类

Thread来实现的–当生成一个Thread类的对象之后,就产生了一个线程。通过该对象实例,可以启动线程、终止线程,或者暂时挂起线程等Thread类本身只是线程的虚拟CPU,线程所执行的代码是通过方法run()

来完成的,方法run()称为线程体–在一个线程被建立并初始化以后,Java的运行时系统就自动调用run()方法,正是通过run()方法才使得建立线程的目的得以实现第12章

线

程线程的状态线程一共有四种状态:新建(new)、可运行状态(runnable)、死亡(dead)及堵塞(blocked)new

Thread()创建新线程可运行态start()不可运行态stop()stop()死亡yield()stop()run()exitsuspend()sleep()wait()I/O流阻塞resume()notify()/notifyAll()I/O指令等待睡眠挂起阻塞就绪运行第12章

线

程线程的生命周期(续)Newborn:线程已创建,但尚未执行Runnable:(就绪)线程已被调度,按优先级和先到先服务原则在队列中排队等待CPU时间片资源Runnnig:正在运行Blocked:(阻塞)因某事件或睡眠而被暂时性地挂起Dead:正常/强行中断,退出运行状态第12章

线

程12.1 Java中的多线程实现技术12.1.1

线程的生命周期每个Java程序都有一个缺省的主线程,对于Application,主线程是main()方法执行的线索。对于Applet,主线程指挥浏览器加载并执行Java小程序。要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程。新建的线程在它的一个完整的生命周期中通常要经历新生、就绪、运行、阻塞和死亡等五种状态,这五种状态之间的转换关系和转换条件如图12.1所示。第12章

线

程等待睡眠挂起阻塞死亡图12.1

线程的生命周期新建start就绪运行第12章

线

程1.新生状态当用new关键字和某线程类的构造方法创建一个线程对象后,这个线程对象处于新生状态,此时它已经有了相应的内存空间,并已被初始化。处于该状态的线程可通过调用start()方法进入就绪状态。第12章

线

程2.就绪状态处于就绪状态的线程已经具备了运行的条件,但尚未分配到CPU资源,因而它将进入线程队列排队,等待系统为它分配

CPU。一旦获得CPU资源,则该线程就进入运行状态,并自动地调用自己的run方法。此时,它脱离创建它的主线程,独立开始了自己的生命周期。第12章

线

程3.运行状态进入运行状态的线程执行自己的run方法中的代码。若遇到下列情况之一,则将终止run方法的执行。终止操作。调用当前线程的stop方法或destroy方法进入死亡状态。等待操作。调用当前线程的join(millis)方法或wait(millis)方法进入阻塞状态。当线程进入阻塞状态时,在millis毫秒内可由其他线程调用notify或notifyAll方法将其唤醒,进入就绪状态。在millis毫秒内若不唤醒则须等待到当前线程结束。第12章多线程睡眠操作。调用sleep(millis)方法来实现。当前线程停止执行后,则处于阻塞状态,睡眠millis毫秒之后重新进入就绪状态。挂起操作。通过调用suspend方法来实现。将当前线程挂起,进入阻塞状态,之后当其他线程调用当前线程的resume方法后,才能使其进入就绪状态。退让操作。通过调用yield方法来实现。当前线程放弃执行,进入就绪状态。当前线程要求I/O时,则进入阻塞状态。若分配给当前线程的时间片用完时,当前线程进入就绪状态。如当前线程的run方法执行完,则线程进入死亡状态。第12章

线

程4.阻塞状态一个正在执行的线程在某些特殊情况下,如执行了suspend、join或sleep方法,或等待I/O设备的使用权,那么它将让出CPU并暂时中止自己的执行,进入阻塞状态。阻塞时它不能进入就绪队列,只有当引起阻塞的原因被消除时,线程才可以转入就绪状态,重新进到线程队列中排队等待CPU资源,以便从原来终止处开始继续运行。第12章

线

程5.死亡状态处于死亡状态的线程将永远不再执行。线程死亡有两个原因:一是正常运行的线程完成了它的全部工作;二是线程被提前强制性地终止,例如,通过执行stop或destroy方法来终止线程。第12章

线

程创建线程的方法创建线程的方法一——继承Thread类–定义一个线程类,它继承类Thread并重写其中的方法

run()。在初始化这个类的实例时,目标对象target可以为null,表示这个实例本身具有线程体创建线程的方法二——实现Runnable接口–

Runnable是Java中用以实现线程的接口,从根本上讲,任何实现线程功能的类都必须实现该接口实现Runnable接口的方式创建线程与用继承Thread类的方式创建线程无本质差别,但是,由于Java不支持多继承,所以任何类如果已经继承了某一类时,就无法再继承Thread类,这时只能通过实现接口

Runnable的方式创建线程对象。例如,小应用程序已经继承了Applet类,不能再继承Thread类,只能通过Runnable接口实现多线程。第12章

线

程12.1.3创建Thread类的子类,首先是声明子类的构造方法,其次是用自己定义的run()方法去覆盖Thread类的run()方法,即将自己要执行的程序区块写入run()方法中。Thread类的重要方法:run()定义线程的具体操作系统调度此线程时自动执行

如何编程呢?初始时无具体操作内容方法一通过继承Thread类方式创建线程-继承Thread类,定义run()方法第12章

线

程表12.1

Java.lang.Thread的构造方法构造方法说

明public

Thread(

)构造一个新线程,用此方式创建的线程必须覆盖run()方法public

Thread(Runnable

target)构造一个新线程,使用指定对象target的run()方法publicThread(ThreadGroup

group,Runnable

target)在指定的线程组group中构造一个新的线程,使用指定对象target的run()方法public

Thread(String

name)用指定字符串名name构造一个新线程public

Thread(ThreadGroup

group,String

name)在指定的线程组group中用指定字符串名name构造一个新线程public

Thread(Runnable

target,String

name)用指定字符串名name构造一个新线程,使用指定对象target的run()方法public

Thread(ThreadGroup

group,Runnable

target,Stringname)在指定的线程组group中使用字符串名

name构造一个新线程,并使用指定对象

target的run()方法第12章

线

程12.1.2

Thread类的方法Thread类(线程类)是java.lang包中的一个专门用来创建线程和对线程进行操作的类。Java在Thread类中定义了许多方法,帮助我们运用和处理线程。这些方法可分为四组:构造方法。用于创建用户的线程对象,表12.1列出了Thread类的构造方法。run()方法。用于定义用户线程所要执行的的操作。改变线程状态的方法。如start()、sleep()、stop()、suspend()、resume()、yield()和wait()方法等。这是最常用的一组方法。其他方法。如setPriority()、setName()等。第12章

线

程Thread类的有关方法start():由Newborn到Runnable启动线程run():线程在被调度时执行的操作sleep(指定时间):令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队产生例外InterruptedException用try块调用sleep(),用catch块处理例外第12章

线

程Thread类的有关方法(续)suspend()

:挂起线程,处于阻塞状态resume():恢复挂起的线程,重新进入就绪队列排队应用:可控制某线程的暂停与继续方法:设一状态变量suspendStatus=false(初始)暂停:if(!suspendStatus){T.suspend();

suspendStatus=true;}继续:if(suspendStatus){T.resume();

suspendStatus=false;}第12章

线

程Thread类的有关方法(续)yield():对正在执行的线程若就绪队列中有与当前线程同优先级的排队线程,则当前线程让出CPU控制权,移到队尾若队列中没有同优先级的线程,忽略此方法stop()强制线程生命期结束isAlive():返回boolean,表明是否还存在第12章

线

程Thread类方法总结启动线程:start()有关线程执行的控制:stop()、suspend()、resume()有关调度控制Thread.sleep(10);//低优先级的线程也可以获得执行Thread.yield();//同优先级的线程可以获得执行suspend();//暂停本线程第12章

线

程表12.2

Java.lang.Thread的常用方法常用方法说

明public

void

run(

)此线程的线程体,在启动该线程后调用此方法。可以通过使用Thread类的子类来重载此方法public

synchronized

void

start(

)启动线程的执行,此方法引起run()方法的调用,调用后立即返回。如果已经启动此线程,就抛出

IllegalThreadedStateException异常public

static

Thread

currentThread(

)返回当前处于运行状态的Thread对象public

static

void

yield(

)使当前执行的Thread对象退出运行状态,使其进入等待队列public

static

void

sleep(longmillis)throwsInterruptedException使当前执行的线程睡眠millis毫秒。如果另一个线程已经中断了此线程,就抛出

InterruptedException

异常第12章

线

程表12.2

Java.lang.Thread的常用方法常用方法说明public

static

voidsleep(longmillis,int

nanos)throws

InterruptedException使当前执行的线程睡眠millis毫秒和附加的nanos毫秒。如果另一个线程已经中断了这个线程,则抛出InterruptedException

异常public

final

void

stop(

)停止线程的执行通过抛出对象停止线程的执行。正常情况下,用public

final

synchronized

void户应该在调用stop方法时不用任何参数。但是,在stop(Throwable

o)某些特殊的环境中,通过stop方法来结束线程,并可抛出另一个对象public

void

interrupt(

)中断一个线程第12章

线

程表12.2

Java.lang.Thread的常用方法public

static

boolean

interrupted(

)询问线程是否已经被中断public

Boolean

isInterrupted(

)询问另一个线程是否已经被中断public

void

destroy(

)消毁一个线程public

final

boolean

isAlive(

)返回表示该线程活动状态的boolean值public

final

void

suspend(

)挂起这个线程的执行public

final

void

resume(

)恢复这个线程的执行,此方法仅在使用suspend()后才有效public

final

void

setPriority(intnewPriority)设置该线程的优先级,如果优先级不在MIN-

PRIORITY

MAX-PRIORITY

的范围内,就抛出IllegalArgumentException异常第12章

线

程表12.2

Java.lang.Thread的常用方法public

final

int

getPriority(

)获取并返回此线程的优先级public

final

void

setName(Stringname)设置该线程名为namepublic

final

String

getName(

)获取并返回此线程名public

final

ThreadGroup

getThreadGroup(

)获取并返回此线程组public

static

int

activeCount(

)返回此线程组中当前活动的线程数量public

static

int

enumerate(Thread

tarray[

])将此线程组中的每一个活动线程拷贝到指定的数组tarray中,返回放到此数组中线程的数量public

final

synchronized

void

join(longmillis)throws

InterruptedException等待此线程死亡,可以指定一个以millis给出的等待时间(以毫秒为单位),如果为0则表示永远等待,如果另一个线程已经中断这个线程,那么就抛出Interrupted-Exception异常第12章

线

程public

final

synchronized

voidjoin(long

millis

,intnanos)throwsInterruptedException等待此线程死亡,可以给出millis等待时间和nanos附加时间。如果另一个线程已经中断此线程,则抛出InterruptedException异常public

final

void

join(

)throwsInterruptedException无限期等待此线程死亡,如果另一个线程已经中断此线程,则抛出InterruptedException异常public

final

boolean

isDaemon(

)返回线程是否为daemon线程public

void

check

Access(

)检查当前线程是否允许访问此线程组。如果不允许,就抛出SecurityException异常public

String

toString(

)返回这个线程的字符串表示,包括该线程的名字,优先级和线程组,覆盖Object类中的toString方法表12.2

Java.lang.Thread的常用方法第12章

线

程方法一从Thread类派生出一个子类,在类中一定要实现run()class

Lefthand

extends

Thread

{public

void

run(){……}}然后用该类创建一个对象Lefthand

left

=

newLefthand();用start()方法启动线程

(程序11-1

11-2)left.start();第12章

线

程public

class

myThread

extends

Thread{public

void

run(){while(running){……

//执行若干操作sleep(100);}}public

static

void

main(String

args[]){Thread

t

=

newmyThread();……

//执行若干操作}}第12章

线

程class

Lefthand

extends

Thread{public

void

run(){for(inti=0;i<=5;i++){System.out.println("You

are

Students!");try{//sleep((int)(Math.random()

*

1000));sleep(500);}catch(InterruptedException

e){}}}}class

Righthand

extends

Thread{public

void

run(){for(inti=0;i<=5;i++){System.out.println("I

am

a

Teacher!");try{sleep(300);}catch(InterruptedException

e){}}}}第12章

线

程public

class

ThreadTest{static

Lefthandleft;static

Righthand

right;public

static

void

main(String[]

args){left

=

newLefthand();right

=

new

Righthand();left.start();right.start();}}第12章

线

程【示例程序c12_1.java】 用Thread类的子类创建两个线程对象。import

java.util.*;class

c12_1

extends

Thread

{int

pauseTime;String

name;public

c12_1(inthTime,

StringhStr){pauseTime

=hTime;name

=hStr;}public

void

run(

){第12章

线

程Calendar

now;//Calendar是Java系统提供的日期时间类的类型标识符

int

year,month,date,hour,minute,second;for(int

i=1;i<10;i++)

{try

{now=Calendar.getInstance(

);year=now.get(Calendar.YEAR);//取系统时间//取年值month=now.get(Calendar.MONTH)+1;

//取月值date=now.get(Calendar.DATE);

//取日期值hour=now.get(Calendar.HOUR_OF_DAY);minute=now.get(Calendar.MINUTE);second=now.get(Calendar.SECOND);//取小时值//取分值//取秒值System.out.println(""+name+"时间:"+year+"年"+month+"月"+第12章

线

程date+"日"+hour+"小时"+minute+"分"+second+"秒");//显示时间

Thread.sleep(pauseTime);}catch(Exception

e){System.out.println("线程错误:"+e);}}}static

public

void

main(String

args[

])

{c12_1

myThread1=new

c12_1(2000,"线程A");//A线程执行一次后睡眠2000毫秒

myThread1.start();c12_1

myThread2=new

c12_1(1000,"线程B");//B线程执行一次后睡眠1000毫秒

myThread2.start();}}第12章

线

程方法二:通过实现Runnable接口方式创建线程创建线程对象的另一个途径是实现Runnable接口,而

Runnable接口只有一个方法run(),用户新建线程的操作由这个方法来决定。run()方法必须由实现此接口的类来实现。定义好

run()方法之后,当用户程序需要建立新线程时,只要以这个实现了run()方法的类为参数创建系统类Thread的对象,就可以把用户实现的run()方法继承过来。方法二:自定义类实现Runnable接口使用Thread类的另一构造函自定义类实现Runnable接口数:Thread(Runnable,

String)使用start()启动线程第12章

线

程例:class

A

implements

Runnable{public

void

run(){….}}class

B

{public

static

void

main(String[]

arg){Runnable

a=new

A();Thread

t=new

Thread(a);t.start();}}第12章

线

程【示例程序c12_2.java】 通过创建线程实现“Java

Now!”在屏幕上不停地走动。importjava.awt.*;import

java.applet.*;public

class

c12_2

extends

Applet

implements

Runnable{Thread

th1=null;String

Message="Java

Now!"; //创建字符串对象Font

f=new

Font("TimesRoman",Font.BOLD,24);体对象int

x,y;public

void

init(

){//创建字第12章

线

程x=getSize(

).width; //取applet窗口的宽度,单位为像素y=getSize(

).height/2; //取applet窗口的高度,单位为像素}public

void

start(

){

//创建线程对象实例

if(th1==null){th1=new

Thread(this);th1.start(

);}}public

void

run(

){while(true){第12章

线

程x=x-5;if(x==0)x=getSize(

).width;repaint(); //repaint()方法调用paint()方法重画字符串对

try{th1.sleep(500); //使th1线程睡眠500

毫秒}catch(InterruptedException

e){

}}}public

void

paint(Graphics

g){g.setFont(f);//设置字体

g.drawString(Message,x,y);}}第12章

线

程图12.2

程序c12_2的运行中的一个瞬间第12章

线

程模拟小球例子程序11-3是一个模拟小球平抛和自由落体的例子BallThread.java相应的HTML文档第12章

线

程import

java.awt.*;import

java.awt.event.*;import

java.applet.*;public

class

BallThread

extends

Applet

implements

Runnable{Thread

red,

blue;Graphics

redPen,

bluePen;int

t=0;public

void

init(){red

=

new

Thread(this);blue

=

new

Thread(this);redPen

=getGraphics();bluePen

=getGraphics();redPen.setColor(Color.red);bluePen.setColor(Color.blue);}public

void

start(){red.start();blue.start();}第12章public

void

run(){多

线

程while(true){t=t+1;if(Thread.currentThread()==red){if(t>100)t=0;redPen.clearRect(0,0,110,400);redPen.fillOval(50,(int)(1.0/2*t*9.8),15,15);//s=1/2gt2try{red.sleep(40);}catch(InterruptedException

e){}}else

if(Thread.currentThread()==blue){bluePen.clearRect(120,0,900,500);bluePen.fillOval(120+7*t,(int)(1.0/2*t*9.8),15,15);//s=v0t+1/2gt2try{blue.sleep(40);}catch(InterruptedException

e){}}}}}第12章

线

程两种方法的选择当需要从其他类,如Applet类继承时,使用Runnable当编写简单的程序时,可考虑使用继承Thread类例:RaceApplet.java具体运行结果(线程调度)与平台有关第12章

线

程public

class

Runner

extends

Thread

{

//跑者线程类public

int

tick

=

1;public

void

run()

{while

(tick

<

40000000)tick++;}}//Runner.java//RaceApplet是一个实现了多线程的Appletpublic

class

RaceApplet

extends

Applet

implements

Runnable

{跑线程的个数final

static

int

NUMRUNNERS=2;//定义

final

staticint

SPACING=20;//声明两个 跑线程Runner[]

runners

=

newRunner[NUMRUNNERS];//声明一个画图线程

Thread

updateThread=null;第12章

线

程public

void

init(){

//重载Applet的init()方法

for(int

i=0;

i<NUMRUNNERS;i++){runners[i]

=

newRunner();

//创建 跑线程线程runners[i].setPriority(i+1);

//设优先级first=1,second=2}if

(updateThread

==

null)

{//创建绘图线程,并设优先级为3updateThread

=

new

Thread(this,

"ThreadRace");updateThread.setPriority(NUMRUNNERS+1);}addMouseListener(new

MyAdapter());//注册事件监听者}

//end

ofinit()第12章

线

程//内部事件监听者类,监听鼠标事件class

MyAdapter

extends

MouseAdapter

{//鼠标点击后,开始 跑及绘制线程public

void

mouseClicked(MouseEventevt)

{if

(!updateThread.isAlive())updateThread.start();//启动绘制线程

for(int

i=0;

i<NUMRUNNERS;i++){if

(!runners[i].isAlive())runners[i].start();

//启动 跑线程}}}

//end

of

class

MyAdapter第12章

线

程public

void

paint(Graphics

g){//paint()方法中绘制框架………}

//end

ofpaint()//update()方法中绘制赛跑者的进度,可以消除图画的闪烁

public

void

update(Graphics

g){for(int

i=0;

i<NUMRUNNERS;i++){//画两条线

g.drawLine(SPACING,(i+1)*SPACING,SPACING

+

(runners[i].tick)/100000,

(i+1)*SPACING);}}

//end

of

update()第12章

线

程publicvoid

run(){//实现Runnable接口的run()方法

while(true){repaint();

//重新绘制,自动调用update()方法

try{Thread.sleep(10);

//休眠,把执行机会让给低优先级线程}

catch

(InterruptedException

e)

{

}}}

//end

of

run()public

void

stop(){//重载Applet的stop()方法

for(int

i=0;

i<NUMRUNNERS;i++){if

(runners[i].isAlive())runners[i]

=null;

//中止 跑线程}if

(updateThread.isAlive())

updateThread=null;

//中止绘图线程}

//end

ofstop()}

//RaceApplet.java第12章

线

程线程的启动通过Thread类中方法start()来启动在程序11-3中,只要执行:red.start();blue.start();第12章

线

程线程的操作方法start()启动线程对象;run()用来定义线程对象被调度之后所执行的操作,用户必须重写run()方法;yield()强制终止线程的执行;isAlive()测试当前线程是否在活动;sleep(int

millsecond)使线程休眠一段时间,时间长短由参数所决定;void

Wait()使线程处于等待状态;第12章多

线

程线程的调度线程调度通常是抢占式,而不是时间片式–抢占式调度是指可能有多个线程准备运行,但只有一个在真正运行。一个线程获得执行权,这个线程将持续运行下去,直到它运行结束或因为某种原因而阻塞,再或者有另一个高优先级线程就绪,最后一种情况中称为低优先级线程被高优先级线程所抢占。12.2

多线程的管理第12章

线

程优先级策略优先级高的先执行,优先级低的后执行多线程系统会自动为每个线程分配一个优先级,缺省时,继承其父类的优先级任务紧急的线程,其优先级较高同优先级的线程按“先进先出”的原则第12章

线

程线程优先级Thread类三个与线程优先级有关的静态量MAX_PRIORITY:最大优先权,值为10;MIN_PRIORITY:最小优先权,值为1;NORM_PRIORITY:默认优先权,值为5Thread类中几个常用的有关优先级的方法Void

setPriority(int

newPriority)

//重置线程优先级Int

getPriority()Static

void

yield()//获得当前线程的优先级//使当前线程放弃执行权第12章

线

程【示例程序c12_3.java】 创建三个线程A、B、C,根据优先级确定线程的执行顺序。class

c12_3{public

static

void

main(String

args[

]){ Thread

First=new

MyThread("A"); //创建A线程First.setPriority(Thread.MIN_PRIORITY);

//A线程优先级为1Thread

Second=newMyThread("B"); //创建B线程Second.setPriority(Thread.NORM_PRIORITY+1); //B线程优先级为6Thread

Third=new

MyThread("C"); //创建C线程第12章

线

程Third.setPriority(Thread.MAX_PRIORITY);//C线程优先级为10First.start();Third.start();Second.start(

);}}class

MyThread

extends

Thread{String

message;MyThread(String

message){

this.message=

message;}public

void

run(

){

for(inti=0;i<2;i++)System.out.println(message+"

"+getPriority(

));}}第12章

线

程该程序的运行结果如下:C

10

C

10

B

6

B

6

A

1

A

1从程序的运行结果中可以看出,虽然线程A在程序中最先调用start()方法进入就绪状态,但由于它的优先级是三个线程中最低的,所以最后才得以执行。第12章

线

程线程的调度被阻塞的线程按次序排列,组成一个阻塞队列。所有就绪但没有运行的线程则根据其优先级排入一个就绪队列CPU空闲时,如果就绪队列不空,队列中第一个具有最高优先级的线程将运行当一个线程被抢占而停止运行时,它的运行态被改变并放到就绪队列的队尾;同样,一个被阻塞(可能因为睡眠或等待I/O设备)的线程就绪后通常也放到就绪队列的队尾为保证给其他线程留有执行的机会,可以通过间隔地调用sleep()第12章

线

程线程的调度例例11-4public

class

xyz

implements

Runnable{public

void

run(){while(true){……

//执行若干操作//给其他线程运行的机会try{Thread.sleep(10);}catch(InterruptedException

e){//该线程为其他线程所中断}}}}第12章

线

程sleep()是静态方法,可以通过Thread.sleep(x)直接引用–

x指定了线程在再次启动前必须休眠的最小时间,毫秒为单位–可能引发中断异常InterruptedException,因此要进行捕获和处理–“最小时间”是因为这个方法只保证在一段时间后线程回到就绪态,至于它是否能够获得CPU运行,则要视线程调度而定,所以,通常线程实际被暂停的时间都比指定的时间要长第12章

线

程yield()可以给其他同等优先级线程一个运行的机会如果在就绪队列中有其他同优先级的线程,

yield()把调用者放入就绪队列尾,并允许其他线程运行;如果没有这样的线程,则yield()不做任何工作sleep()调用允许低优先级进程运行,而yield()方法只给同优先级进程以运行机会第12章

线

程线程的基本控制结束线程–当一个线程从run()方法的结尾处返回时,它自动消亡并不能再被运行,可以将其理解为自然死亡–利用stop()方法强制停止,可以将其理解为强迫死亡,这种方法必须用于Thread类的特定实例中第12章

线

程强迫死亡Stoppublic

class

xyz

implements

Runnable{……

//执行线程的主要操作}public

class

ThreadTest{public

static

void

main(String

args[]){Runnable

r

=

newxyz();Thread

t

=

newThread(r);t.start();//相应的操作if

(time_to_kill){t.stop();}}}第12章

线

程结束线程例利用Thread类中的静态方法currentThread()来引用正在运行的线程,见例11-6–在这个例子中,执行stop()将破坏当前的运行环境,因而run()中的循环在此情况下将不再运行例11-6public

class

xyz

implements

Runnable{public

void

run(){while(true){……//执行线程的主要操作if

(time_to_die){Thread.currentThread().stop();}}}}第12章

线

程检查线程isAlive()–获取一个线程是否还在活动状态的信息。–活动状态不意味着这个线程正在执行,而只说明这个线程已被启动,并且既没有运行

stop(),也尚未运行完方法run()。第12章

线

程挂起线程暂停一个线程称为挂起。在挂起之后,必须重新唤醒线程进入运行挂起线程的方法sleep()线程不是休眠期满后就立刻被唤醒,因为此时其他线程能正在执行,重新调度只在以下几种情况下才会发生:被唤醒的线程具有更高的优先级;正在执行的线程因为其他原因被阻塞;程序处于支持时间片的系统中suspend()和resume()join()

:引起现行线程等待,直至方法join所调用的线程结束第12章

线

程程序11-4suspend()和resume()程序11-4说明:线程t在运行到suspend()以后被强制挂起,暂停运行,直到主线程调用t.resume()时才被重新唤醒;一个线程只能被不同于它自身的线程所唤醒,因为在执行suspend()方法以后,这个线程其后的代码都不会被执行到,而该线程又依赖于其后的resume语句将其唤醒,所以千万不能将resume()用在已被挂起的线程语句中第12章

线

程class

xyz

implements

Runnable{public

void

run(){……//执行线程的主要操作//暂停线程运行,直到被其他线程唤醒Thread.currnetThread().suspend();……

//线程继续运行...}}class

Usexyz{public

static

void

main(String

args[]){Runnable

r

=

newxyz();Thread

t

=

newThread(r);t.start();//暂停当前线程运行,以使得已经被阻塞得xyz的实例得以运行Thread.sleep(1000);//xyz实例被suspend()方法暂停,将控制权返还给主线程,//并由主线程重新唤醒线程tt.resume();Thread.yield();}}第12章

线

程线程对内存、数据的共享如push(a):idx++;data[i]=aclass

Stack{int

idx

=

0;char

data[]

=

newchar[6];public

void

push(char

c){data[idx]

=

c;idx

++;}public

charpop(){idx

--;returndata[idx];}}12.2.3

线程同步1、问题的提出线程执行的不确定性引起执行结果的不稳定如线程A:

A1-A2

线程B:

B1-B2pop():

取出data[i];idx--;第12章

线

程一个代表栈的类例11-8错误情况1–假设线程a负责加入字符,线程b负责移出字符。线程a刚刚加入了一个字符,例如是r,但是尚未递增索引值,由于某种原因,恰恰这时它被抢占了:buffer |

p|

q|

r|

|

|

|idx=2

^idx=3第12章

线

程错误情况2如果此时线程b正在等待移出一个字符,当线程a处于等待状态时,线程b就得到了运行机会。这样,在进入方法pop()时,数据状态已经是错误的。pop方法将继续递减索引值,idx变为1buffer |

p|

q|

r|

|

|

|idx=1

^操作后将返回字符

“q”,而忽略字符

“r”第12章

线

程例子说明产生这种问题的原因是对共享资源访问的不完整性–完整性称为共享数据操作的同步,共享数据叫做条件变量解决问题的方法–禁止线程在完成代码关键部分时被切换–提供一个特殊的锁定标志来处理数据 (Java采用的方法

)第12章

线

程对象的锁定标志“对象互斥锁” (又称为监视器、管程)–实现不同线程对共享数据操作的同步。“对象互斥锁”阻止多个线程同时访问同一个条件变量。Java可以为每一个对象的实例配有一个“对象互斥锁”实现“对象互斥锁”两种方法–用关键字volatile来声明一个共享数据(变量);–用关键字synchronized来声明一个操作共享数据的方法或一段代码。第12章

线

程例11-9class

stack{int

idx

=

0;char

data[]

=

newchar[6];public

void

push(char

c){synchronized

(this){data[idx]

=

c;idx

++;}}…………public

charpop(){synchronized

(this){idx

--;returndata[idx];}}}增加了一个对synchronized(this)的调用增加了一个对

synchronized(this)的调用第12章

线

程过程图示线程1...因等待同步资源而挂起的线程队列第12章

线

程同步方法用synchronized来标识的代码段或方法即为“对象互斥锁

”锁住的部分。如果一个程序内有两个或以上的方法使用synchronized标志,则它们在同一个“对象互斥锁”管理之下一般情况下,都使用synchronized关键字在方法的层次上实现对共享资源操作的同步,很少使用volatile关键字声明共享变量第12章

线

程synchronized()语句写法标准写法(public

void

push(char

c){}}简pu洁blic的sy写nch法ronized

void

push(char

c){……}把synchronized用做方法的修饰synch更ron为ized妥(th字帖is){,)则整个方法都将视作同步…………块,这可能会使持有锁定标记

的时间比实际需要的时间要长,从而降低效率第12章

线

程死锁步以线程运行完同继续运行,所每个线程都程序块。而哪个线程都线程2pen线程1note死锁情况发生在一个线程等待另一个线程所持有的锁,而第二个线程又在等把“待pen第”给一我,个我线程持有的锁的时候。不能继续才运能给行你,“no除te”非另一恰恰因为哪个线程都不能无法运行完同步程序块把“note”给我,我才能给你“pen”第12章

线

程死锁问题程序死锁问题见程序11-6第12章

线

程解决死锁问题的方法给资源施加排序制定一个规则来决定以何种顺序来获得这些锁,并在整个程序中遵循这个顺序

–参考操作系统方面的相关书籍第12章

线

程线程交互为什么两个线程需要交互呢?–涉及到多线程间共享数据操作时,除了同步问题之外,还会遇到如何控制相互交互的线程之间的运行进度,即多线程的同步问题–生产者——消费者问题生产者比消费者快时,消费者漏掉一些数据消费者比生产者快时,消费者取相同的数据生产者消费者共享对象putget第12章

线

程在这个问题中,两个线程要共享货架这一临界资源,需要在某些时刻(货空/货满)协调它们的工作,即货空时消费者应等待,而货满时生产者应等待。为了不致于发生混乱,还可进一步规定:当生产者往货架上放货物时不允许消费者取货物,当消费者从货架上取货物时不允许生产者放货物。这种机制在操作系统中称为线程间的同步。在同步机制中,将那些访问临界资源的程序段称为临界区。第12章

线

程问题的解决同步:用synchronized关键字前缀给针对共享资源的操作加锁;同步方法、同步块synchronized

void

push() synchronized

int

pop()实现机制:管程线程间需协调与通讯:生产者/消费者问题wait()与notify()Object类的方法:public

final

voidwait():令当前线程挂起并放弃管程,同步资源解锁,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问notify()唤醒正在排队等待资源管程的线程中优先级最高者,使之执行并拥有资源的管程wait()+notify()+标志变量:可协调、同步不同线程的工作第12章

线

程在Java系统中,临界区程序段是用关键字“synchronized”来标注,并通过一个称为监控器的系统软件来管理的。当执行被冠以

synchronized的程序段即临界区程序时,监控器将这段程序(访问的临界资源)加锁,此时,称该线程占有临界资源,直到这段程序执行完,才释放锁。只有锁被释放后,其他线程才可以访问这些临界资源。用关键字synchronized定义临界区的语句形式是:synchronized

(expression)

statement其中,expression代表类的名字,它是可选项;statement可以是一个方法,也可以是一个语句或一个语句块,最常见的是一个方法。下面通过一个例子来说明线程的同步问题。第12章

线

程Wait_Notify

程序CubbyHole.java创建用户的线程子类Producer:产生数据(存数据);

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论