8线程Word下载.docx

上传人:b****6 文档编号:18931422 上传时间:2023-01-02 格式:DOCX 页数:17 大小:39.37KB
下载 相关 举报
8线程Word下载.docx_第1页
第1页 / 共17页
8线程Word下载.docx_第2页
第2页 / 共17页
8线程Word下载.docx_第3页
第3页 / 共17页
8线程Word下载.docx_第4页
第4页 / 共17页
8线程Word下载.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

8线程Word下载.docx

《8线程Word下载.docx》由会员分享,可在线阅读,更多相关《8线程Word下载.docx(17页珍藏版)》请在冰豆网上搜索。

8线程Word下载.docx

与进程不同,线程不能单独运行,它必须依赖于某个进程,在进程中运行。

同时,同一进程中的不同线程,虽然有各自不同的执行流,却共享着一定的数据空间和系统资源。

3、为什么要使用线程

在基于进程的多任务中,程序是能够被调度派的最小代码单元,特征是允许计算机同时运行两个或多个程序。

比如:

一边听mp3,一边编辑文档。

在基于线程的多任务中,线程是可供调度的最小代码单元,意味着单个程序可以同时完成两个或者更多的任务。

一个很好的例子便是“Quit”或“退出”按钮——我们并不希望在程序的每一部分代码中都轮询这个按钮,同时又希望该按钮能及时地作出响应(使程序看起来似乎经常都在轮询它)。

事实上,多线程最主要的一个用途就是构建一个“反应灵敏”的用户界面。

二、线程的状态及其生命周期

新建的线程在它的一个完整的生命周期中通常要经历如下的五种状态:

1、新建态(new)

当用new语句新创建一个线程时,该线程并不会立即运行,但会分配相应的系统资源(比如内存),此时,线程处于新建状态。

如果需要启动线程,调用线程的start()方法。

ThreadObject.start();

2、就绪态(可运行状态rannable)

一个线程启动后,就进入可运行状态。

在某一时刻,处于可运行状态的线程并不一定会运行,将进入线程队列排队等待CPU服务,需要根据操作系统是否给该线程分配时间而定,只有当该线程在某个CPU周期内获得CPU使用权,才会行该线程。

3、运行态

当某个线程获得了CPU使用权后,该线程进入CPU运行,其状态就为运行态。

(run方法)

4、阻塞/挂起态(blocked)

阻塞/挂起状态也就是线程被挂起暂时不被执行,等到适当的时候再运行。

处于该状态的依然是活动的(可以用Thread类的isAlive方法来判断,该状态和可运行状态都是活动的)。

下面的操作会导致线程阻塞。

调用sleep方法让线程睡眠;

调用wait方法让线程等待;

线程调用suspend方法暂停线程(Java2不推荐使用该方法);

线程调用只有在I/O操作完成时才返回的操作;

线程试图锁住一个被其他线程锁住的对象。

5、终止态(dead)

处于死亡状态的线程不具有继续运行的能力。

线程死亡的原因有二,一个是正常运行的线程完成了它的全部工作,即执行完了run()方法的最后一个语句并退出,另一个是线程被提前强制性地终止。

线程死亡后就处于终止状态。

另外,某个异常终止run方法的执行(比如调用stop方法强制终止该线程),也会导致线程处于终止状态。

运行态

终止态

阻塞态

就绪态

新建态

三、线程调度及优先级

处于就绪状态的线程首先进入就绪队列排队等候CPU资源,同一时刻在就绪队列中的线程可能有多个。

多线程系统会给每个线程自动分配一个线程的优先级,任务较紧急重要的线程,其优先级就较高;

相反则较低。

在线程排队时,优先级高的线程可以排在较前的位置,能优先享用到CPU资源,而优先级较低的线程则只能等到排在它前面的高优先级线程执行完毕之后才能获得处理器资源。

对于优先级相同的线程,则遵循队列的“先进先出”的原则,即先进入就绪状态排队的线程被优先分配到处理器资源,随后才为后进入队列的线程服务。

当一个在就绪队列中排队的线程被分配到处理器资源而进入运行状态之后,这个线程就称为是被“调度”或被线程调度管理器选中了。

线程调度管理器负责管理线程排队和处理器在线程间的分配,一般都配有一个精心设计的线程调度算法。

在Java系统中,线程调度依据优先级基础上的“先到先服务”原则。

协作式8线程模型允许线程自己决定什么时候放弃处理器来等待其他的线程。

程序开发员可以精确地决定某个线程何时会被其他线程挂起,允许它们与对方有效地合作。

缺点在于某些恶意或是写得不好的线程会消耗所有可获得的CPU时间,导致其他线程“饥饿”。

Java中线程的调度是抢占式的,如果当前某个线程在执行过程中,一个具有更高优先级的线程进入“就绪态”,那么这个高优先级的线程就会被立即执行,相同优先级的线程轮流被执行。

finalvoidsetPriority(intnewPriority)设置线程的优先级;

finalintgetPriority()获得线程的优先级;

线程的优先级用数字来表示,从低到高依次为1~10。

Thread.MIN_PRIORITY1

ThreadMAX_PRIORITY10

Thread.NORM_PRIORITY5(default)

新建线程将继承创建它的父线程的优先级。

父线程是指执行创建新线程对象语句的线程,它可能是程序的主线程,也可能是某一个用户自定义的线程。

一般情况下,主线程具有普通优先级。

另外,用户可以通过调用Thread类的方法:

setPriority(inta)来修改系统自动设定的线程优先级,使之符合程序的特定需要。

四、线程的建立

1、主线程

每个Java程序都有一个缺省的主线程。

对于应用程序,主线程是main()方法执行的线索(之所以称为主线程,而不称做进程,是因为我们的程序在执行时,我们的计算机仍可以浏览网页,也可以后台打印等)。

对于Applet,主线程指挥浏览器加载并执行Java小程序。

Java语言使用Thread类及其子类的对象来表示线程,上面说过,Java程序启动时,一个线程立刻开始运行,这个线程通常称为程序的主线程,因为是它是程序开始后执行的线程。

它是产生其他子线程的线程,经常是最后一个结束执行的线程,因为它完成各种关闭操作。

尽管主线程在程序开始时自动创建,它也能通过Thread对象来控制。

为了达到这个目的,必须通过调用currentThread()方法获得它的引用,这是Thread公共的静态方法。

staticThreadcurrentThread()

一旦获得对主线程的引用,就能像其他线程一样控制它。

MainThread1.java

当println()的参数为t时,它会按照线程的名字、优先级和所属线程组的名字的顺序来输出。

Default,主线程名字是main,优先级是5,main也是线程所属于的线程组的名字。

2、继承Thread类

publicclassXextendsThreadThread1.java

Thread类的构造方法

publicThread(ThreadGroupgroup,Runnabletarget,Stringname);

其中group为该线程所属的线程组,target是执行线程的目标对象,它必须实现接口Runnable(Thread类实现了该接口,必须重置run()方法),name是该线程的线程名。

前面构造方法的参数都可以为空,可以组合出多种不同的构造方法,如果group为空,则该线程与创建它的线程属于同一个线程组;

如果traget为空,那么目标对象就是该线程本身。

start()方法启动线程,程序中并没有显示调用run()方法,但执行结果却是执行run()方法的结果,这说明线程启动后,调用目标对象的run()方法。

练习:

按动下面的按钮,上面的文本框自动实现数字的累加,文本区可自由编辑,要求继承Thread类实现,并且全部以内部类的形式实现。

DeadLoop1.java

3、实现Runnable接口

Runnable接口只有一个方法run(),所有实现Runnable接口的用户类都必须具体实现这个run()方法,为它提供方法体并定义具体操作。

Runnable接口中的这个run()方法是一个较特殊的方法,它可以被运行系统自动识别和执行,具体地说,当线程被调度并转入运行状态时,它所执行的就是run()方法中规定的操作。

所以,一个实现了Runnable接口的类实际上定义了一个主线程之外的新线程的操作,而定义新线程的操作和执行流程,是实现多线程应用的最主要和最基本的工作之一。

publicclassXimplementsRunnableThread2.java

测试打字速度,要求实现Runnable接口。

Testing.java

五、Thread类常用方法

1、finalbooleanisAlive()判断线程是否结束;

ThreadIsAlive.java

2、publicvoidinterrupt()向调用线程发出中断请求,要求其停下来,它不能对休眠状态的线程发出中断请求。

ThreadInterrupt.java

3、booleanisInterrupted()判断调用线程是否被中断。

StaticBooleaninterrupted()用于测试当前线程是滞已经被中断。

调用这个静态方法有个副作用,即它会将当前线程的“中断”状态改为false。

4、finalvoidjoin()throwsInterruptedException等待调用线程执行完毕。

ThreadJoin1.java

5、voidsuspend()让当前线程暂停(不推荐使用,不释放同步锁)。

6、voidresume()恢复暂停的线程(不推荐使用,不释放同步锁)。

跑马。

ThreadSuspendResume.java

7、voidsleep(loginmillis)让前线程睡眠millis毫秒。

8、voidstart()启动一个线程。

9、voidstop()强制线程结束(不推荐使用);

只有sleep时不能调用,因为它正处于休眠的状态如果你去终止它的话,它将会得到一个异常;

这个异常是:

(IllegalThreadStateException)。

Stop方法会导致线程停止运行,抛出一个异常对象类型ThreadDeath。

该异常条件会终止所有正在运行的方法,包括run方法,那么synchronized方法中任何没有被抓取的异常事件都可能导致该方法提前终止运行,并且导致对象受损。

10、staticvoidyield()自愿将执行权交给其他线程,在调用一个yield()方法之前最好先用一个isAlive()方法去看一下之线程是否已经死亡了,如果杀死一个不存在的线程会造成系统错误。

ThreadYeild1.java

11、voidsetName(StringthreadName)设置线程的名字。

12、StringgetName()获得线程的名字。

13、Thread.currentThread()返回当前线程的一个引用。

14、publicbooleanisDaemon()判断当前线程是否是精灵线程(守护线程)。

15、publicbooleansetDaemon(booleanone)把当前的线程设计为精灵线程;

如果你想把一个线程设为精灵线程的话,这样你就可以one设为true,这样你再用一个线程的对象去调用它则会把此对象设为精灵线程。

Daemons.java

精灵线程(守护线程)是指:

存在于系统的后台,作无限的循环的程序,是一种“在背景提供通用性服务”的线程,它并不属于程序本体,也不存在什么生命周期的,它是靠虚拟机来destroy它的,什么时候虚拟机为做这件事呢,当所有的用户线程都没有的时候,虚拟机为把所有的精灵线程destroy掉,精灵线程的优先级要低于用户线程的优先级,但是一个用户线程可以定义为一个精灵线程,它是在此线程没有调用start的方法之前,最好的两个地方把用户线程设为精灵线程,一个是main()方法的时候,另一个是在类的构造函数里时候直接用setDaemon(true)它将会把此线程设为精灵线程,但是如果说一个精灵线程想把它设为用户线程,它是不可以的,因为一个精灵线程它没有生命力的,而且它是靠一个死循环来进行工作的;

如果一个线程已设计为精灵线程之后再想把它设成用户线程已经是不可能的事了,因为前面所讲的一切。

继承Object类的方法,下面的方法都会释放同步锁(替代原来的方法)

finalvoidnotify()恢复一个等待调用对象的线程的执行。

finalvoidnotifyAll()恢复所有等待调用对象的线程的执行。

finalvoidwait()throwsInterruptedException通知调用线程进入休眠状态。

finalvoidwait(longmilliseconds)等等m毫秒。

finalvoidwait(longmilliseconds,intnanoseconds)等待另一个线程的执行,毫秒加纳秒。

六、多线程

多线程能够让你写出最大限度利用CPU的非常有效的程序,因为空闲的时间被保持在最小。

这在Java操作交互的、网络环境下的应用时显得非常重要,因为空闲时间是普遍的。

例如:

数据通过网络传输的速度要大大低于计算机处理它的速率,即使是本地的文件系统资源,其读写速度也大大低于CPU的处理速度。

当然,用户输入的速度也是比较慢的。

在传统的单线程环境下,你的程序必须等待这些任务中的每个执行完才能继续下一个,即使CPU在大多数时候是空闲的。

多线程使你能够有权使用空闲时间并利用它。

Java多线程的好处是取消了单线程的主循环和轮流检测机制,一个线程可以暂停而不阻止系统其他部分的执行。

TwoThread1.java

三个文本框,两个按钮,按动第一个钮,第一个文本框显示从0向上增1;

按动第二个钮,第二个文本框显示从0向下减1;

同时还得在第三个文本框中显示两个文本框的和。

DeadLoop3.java

线程组

为便于线程的管理,Java将多个线程集成到一个线程组,每个线程都是线程组中的一个成员。

通过线程组可以同时对其中的多个线程进行某个操作如:

启动、中断等。

如果一个线程在构造时没有指定隶属于哪个线程组,Java默认为它与创建它的线程属于同一个线程组(同属于自己的系统线程组)。

不过,只能访问我们自己的系统线程组树下所分出去的线程,却无法访问其他的系统线程组树。

线程组通过java.lang包中的ThreadGroup类来实现。

ThreadGroup(Stringname);

ThreadGroup(ThreadGroupparent,Stringname);

name是线程组名,parent是表示该线程组的父线程组。

创建一个新的线程组:

如:

ThreadGroupmyThreadGroup=newThreadGroup(“newThreadGroup”);

创建一个线程隶属于某个线程组:

ThreadmyThread=newThread(myThreadGroup,“threadofmyThreadGroup”);

ThreaGroup类中的常用方法

intactiveCount()获得线程组中的活动线程数;

intenumerate(Thread[]list)得到线程组中的活动线程及其总数;

StringgetName()获得当前线程组名;

ThreadGroupgetParent()获得当前线程组的父线程组;

voidinterrupt()中断当前线程组以及其中所有的线程。

在Thread类中可以通过getThreadGroup()方法获得该线程所属的线程组:

ThreadGroupgetThreadGroup();

ThreadGroupgroup=myThread.getThreadGroup();

七彩撞球。

ThreadGroup1.java

七、同步

1、同步的概念

Java使我们可以创建多个线程,在处理多线程问题时,我们必须注意这样一个问题:

当两个或多个线程同时访问同一个变量,并且一个线程需要修改这个变量。

我们应对这样的问题作出处理,否则可能发生冲突(SycTest1.java再加上synchronized测试),这时它们需要某种方式来确保资源在某一时刻只能被一个线程使用,达到这个目的方式称之为同步。

比如一个工资管理负责人正在修改雇员的工资表,而一些雇员也正在领取工资,如果容许这样做必然出现混乱。

因此工资管理负责人正在修改工资表时(包括他喝杯茶休息一会),将不容许任何雇员领取工资,也就是说这些雇员必须等待。

在处理线程同步时,要做的第一件事就是要把修改数据的方法用关键字:

synchronized来修饰(可以理解为“上锁”)。

一个方法使用关键字synchronized修饰后,当一个线程A使用这个方法时,其他线程想使用这个方法时就必须等待,直到线程A使用完该方法。

2、同步的方式

(1)同步方法:

锁定对象

publicsynchronizedvoidmethod(){……}。

当某个线程调用一个对象中被同步的方法访问某个数据时,该对象将转为“锁定”状态,其他的线程不能调用同一个对象执行任何被同步的方法访问数据。

必须等第一个线程调用完毕,其他的线程才能调用这个方法。

这样就避免了多个线程同时访问某个对象的共享数据。

其他线程仍然可以自由调用已经锁定的对象上的“非同步”方法。

如果某个线程拥有一个对象的锁,并且它调用了同一个对象上的另一个“同步”方法,那么该线程将自动被赋予对方法的访问权。

只有当该线程退出上一个“同步”方法时,它才会释放该锁。

(从技术上讲,每个对象都有一个锁计数器,用于计算锁的所有者调用了多少次“同步”方法。

每次调用一个,计数器值便会递增,相反,则递减,当锁计数器的值到达0时,该线程便放弃对该锁的所有权。

我们可以拥有同一个类的不同对象,每个对象可以被不同的线程锁定。

注意:

如果一个线程持有对多个对象的锁,那么调用wait方法将只能打开wait方法被调用的这个对象上的锁。

这就意味着被中断的线程可以继续锁定其他的对象,在该线程被解除中断之前,这些对象的锁将不会被打开。

这是一种危险的情况,应该法避免。

当一个线程使用的同步方法中用到某个变量,而此变量又需要其它线程修改后才能符合本线程的需要,那么可以在同步方法中使用wait()方法,使本线程等待,并允许其它线程使用这个同步方法。

其它线程如果在使用这个同步方法时不需要等待,那么它使用完这个同步方法的同时,用notifyAll()方法通知所有的由于使用这个同步方法而处于等待的线程结束等待,再次使用这个同步方法(注意,如果使用notify(),那么只是通知第一个处于等待的线程结束等待)。

如果只同步其中的一个方法,那么另一个就可以自由忽视对象的锁定,并可无碍地调用。

所以必须记住一个重要的规则:

对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized,否则就不能正常地工作。

练习:

模拟三个人排队买票,张某、李某和赵某买电影票,售票员只有一张五元的钱,电影票5元钱一张。

张某拿二十元一张的人民币来买票,李某拿一张10元的人民币来买票,赵某拿一张5元的人民币来买票。

(测试三个人以不同顺序到来时程序能否正确执行)

WaitNotify.java

synchronized不能继承,父类的方法是synchronized,那么其子类重载方法中就不会继承“同步”。

(2)同步块:

在方法内部锁定数据对象

synchronized(要锁定的数据对象){……}。

有时候,锁定一个对象,并且获取对该对象少数几个指令的独占访问权,而不编写新的同步方法是非常有用的。

可以使用同步块来获取这种访问权。

publicvoidrun(){

……

synchronized(obj){

……

}

同步块将在其他线程能够调用被锁定的对象(obj)上的同步方法之前运行完毕,这种方法可以降低“系统开销”。

DeadLoop2.java

测试方案:

第一:

没有同步的时候,sleep时间相同;

第二:

没有同步的时候,sleep时间不同;

第三:

同步其中一个方法,sleep时间相同;

第四:

同步其中一个方法,sleep时间不同;

第五:

使用锁定对象的方法测试;

第六:

使用同步块的方法测试。

(3)同步静态方法

如果某个类只会产生一个对象(通常用于必须做到全局统一的管理型对象,打印假脱机程序、数据库连接管理器等等)

publicclassA{

privatestaticAinstance;

publicstaticAgetA(){

if(instance==null)

instance=newA();

privateA(){……}

这时,getA()方法对于线程来说并不是个非常安全的方法。

如果instance字段被设置之前,某个线程调用了getA方法,并且在构造方法运行期间被另一个线程抢占运行,那么另一个线程就获得了控制权,并且它将调用getA方法。

由于instance仍然是null,因此该线程就会构造第二个对象。

这是必须要防止出现的情况。

解决这个问题就是同步静态方法。

这样,在另一个线程调用它之前,该方法便完成了运行。

调用一个同步静态方法将会锁定类对象A.class(一个独特的对象类型class),那么该类的所有静态同步方法均被锁定,直到第一个调用结束为止。

3、同步的效率问题

要为同样的数据编写两个方法,所以无论如何都不会给人留下效率很高的印象。

看来似乎更好的一种做法是将所有方法都设为自动同步,并完全消除synchronized关键字(当然,含有synchronizedrun()的例子显示出这样做是很不通的)。

但它也揭示出获取一把锁并非一种“廉价”方案——为一次方法调用付出的代价(进入和退出方法,不执行方法主体)至少要累加到四倍,而且根据我们的具体实现方案,这一代价还有可能变得更高。

所以假如已知一个方法不会造成冲突,最明智的做法便是撤消其中的synchronized关键字。

4、常见的上锁问

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1