Java并发编程第一章Word格式文档下载.docx

上传人:b****5 文档编号:19663684 上传时间:2023-01-08 格式:DOCX 页数:28 大小:50.86KB
下载 相关 举报
Java并发编程第一章Word格式文档下载.docx_第1页
第1页 / 共28页
Java并发编程第一章Word格式文档下载.docx_第2页
第2页 / 共28页
Java并发编程第一章Word格式文档下载.docx_第3页
第3页 / 共28页
Java并发编程第一章Word格式文档下载.docx_第4页
第4页 / 共28页
Java并发编程第一章Word格式文档下载.docx_第5页
第5页 / 共28页
点击查看更多>>
下载资源
资源描述

Java并发编程第一章Word格式文档下载.docx

《Java并发编程第一章Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Java并发编程第一章Word格式文档下载.docx(28页珍藏版)》请在冰豆网上搜索。

Java并发编程第一章Word格式文档下载.docx

每个动作都必须在前一动作结束后才能开

始,除了人为干预造成机器暂时停顿外,前一动作的结束就意味着后一动作的开始。

程序和

机器执行程序的严格一一对应。

(3)程序的执行结果与它的运行速度无关。

也就是说,处理机在执行程序两个动作之

间的停顿不会影响程序的执行结果。

上述特点概况起来就是程序的封闭性和可再现性。

所谓封闭性指的是程序一旦开始运

行,其计算结果就只取决于程序本身,除了人为地改变机器的运行状态或机器故障以外,没

有其它因素能够对程序的运行过程施加影响。

所谓再现性就是当机器在同一数据集上重复执

行同一程序时,机器内部的动作系列完全相同,最后获得的结果也相同。

这种工作方式的特

点是简单、清晰、便于调试程序。

2.资源共享与并行

为了提高计算机系统内各种资源的使用效率,现代计算机系统设计中普遍采用了多道程

序技术。

与单道程序相比,多道程序的工作环境发生了很大变化,主要表现在下列两个方面:

(1)资源共享

资源共享指的是系统中的软、硬件资源不再为单个用户程序独占,而由几道用户程序共

同使用。

于是,这些资源的状态就不再取决于一道程序,而是由多道程序的活动所决定。

就从根本上打破了了一道程序封闭于一个系统中运行的局面。

(2)程序的并发运行

系统中各个部分不再以单纯的串行方式工作。

换言之,在任一时刻系统中不再只有一个

活动,而是存在着许多并行的活动。

从硬件方面看,处理机、各种外设、存储部件常常并行

地进行着工作。

从程序方面看,则可能有若干个作业程序或者同时、或者互相穿插在系统中

并行运行。

这时,机器不再是简单地顺序执行一道程序。

也就是说,一道程序的前一动作结

束后,系统不一定立即执行其后续操作,而可能转而执行其它程序的某一操作。

对于程序中

可以执行的操作也可能不需要等待另一操作结束,系统就开始执行它们。

这样也就打破了程

序执行的顺序性。

同时,多个程序活动可能是在不同的数据集上执行同一个程序,所以程序

以及机器执行程序的活动不再有严格的一一对应关系。

1.1.1.2进程与并发

1.进程的引入

在多道程序工作环境下,一个程序活动不再能独占系统资源,因此也就不再能单独决定

这些资源的状态;

程序和机器执行程序的活动之间也不再有一一对应关系。

总之,程序活动

不再处于一个封闭的系统中,而是和其它程序活动之间存在着相互依赖和制约的关系,因而

呈现出并发、动态以及相互制约这些新的特征。

在这种情况下,程序这个静态的概念已经不

能如实地反映程序活动的这些特征。

为此,六十年代中期MULTICS操作系统的设计者和

E.W.Dijkstra为首的T.H.E操作系统的设计者开始广泛应用进程(process)这一新的概念来描

述系统和用户的程序活动。

“进程”是操作系统的最基本的,也是最重要的概念之一。

这个概念对于操作系统的理

解、描述和设计都具有极其重要的意义。

但是迄今为止对这一概念还没有一个确切统一的描

述。

有人称进程是可以并行运动的计算部分(S.E.Madnick,J.J.Donovan);

有人称进程是一个程

序与其数据一道在计算机上顺序执行时所产生的活动(A.C.Shaw);

有人从调度组织角度出

发,称进程是一个独立的可以调度的活动(Ellis.Cohen,DavidJofferson);

有人则从资源共享和竞

争方面观察,认为进程是一个抽象的实体,当它执行一个任务时将要求分配和释放各种资源

(Peterdenning)。

这些描述都注意到了进程的动态性质,但侧重面不同。

为了突出进程和程序

两个概念的区别和联系,我们对进程作如下描述:

进程是一种活动,它是由一个动作系列组

成,每个动作是在某个数据集上执行一段程序,整个活动的结果是提供一种系统或用户功能。

2.进程与程序的区别

我们再为进程和程序之间的区别和联系作以下几点说明。

1)进程是程序的一次运行活动,属于一种动态的概念。

程序是一组有序的静态指令,

是一种静态的概念。

但是,进程离开了程序也就没有了存在的意义。

因此,我们可以这样说:

进程是执行程序的动态过程,而程序是进程运行的静态文本。

如果我们把一部动画片的电影

拷贝比拟成一个程序,那么这部动画片的一次放映过程就可比为一个进程。

2)一个进程可以执行一个或多个程序。

例如:

一个进程进行C源程序编译时,它要执

行前处理、词法语法分析、代码生成和优化等几个程序。

反之,同一程序也可能由多个进程

同时执行,例如:

上述C编译程序可能同时被几个程序执行,它们对相同或不同的源程序

分别进行编译,各自产生目标程序。

我们再次以动画片及其放映活动为例,一次电影放映活

动可以连续放映几部动画片,这相当于一个进程可以执行几个程序。

反之,一部动画片可以

同时在若干家电影院中放映,这相当于多个进程可以执行几个同一程序。

不过要注意的是,

几家电影院放映同一部电影,如果使用的是同一份拷贝,那么实际上是交叉进行的。

但在多

处理机情况下,几个进程却完全可以同时使用一个程序副本。

3)程序可以作为一种软件资源长期保持着,而进程则是一次执行过程,它是暂时的,

是动态地产生和终止的。

这相当于电影拷贝可以长期保存,而一次放映活动却只延续1~2

小时。

进程需要使用一种机构才能执行程序,这种机构称之为处理机(Processor)。

处理机执行

指令,根据指令的性质,处理机可以单独用硬件或软、硬件结合起来构成。

如果指令是机器

指令,那么处理机就是我们一般所说的中央处理机(CPU)。

3.进程的并发性和不确定性

并发性:

并发可以看成是在系统中同时有几个进程在活动着,也就是同时存在几个程序

的执行过程。

如果进程数与处理机数相同,则每个进程都占用一个处理机。

但更一般的情况

是是处理机数少于进程数,于是处理机就应被共享,在进程间进行切换使用。

如果相邻两次

切换的时间间隔非常短,而观察时间又相当长,那么各个进程都在前进,造成一种宏观上并

行运行的效果。

所以并发处理的真正含义是:

如果我们把系统作为一个整体来观察,则在任

一时刻有若干进程存在于系统的这一部分或那一部分,这些进程都处在其起点和终点之间。

我们把所有这些进程都看成是正在系统中运行着、活跃着。

不确定性:

我们把进程看成是一个动作系列,而每个动作是执行一段程序。

处理机要检

测是否已接获某种需要立即处理的中断信号。

如果已经接到这种信号,则立即停止正在执行

的程序段,转而执行相应的中断处理程序。

在此以后,还要按情况或者恢复继续执行被中断

的程序,或者调度执行另一个进程的程序。

因为中断发生的时间以及频繁程度与系统中许多

经常变化着的不确定因素有关,例如,系统中活跃着的进程的数量以及它们的工作情况,各

种硬件工作速度的细微变化等,所有它们都是不可预测的。

因此,各个进程(也就是各个动

作序列)也就在不可预测的次序中前进。

如果由于进程间相互制约关系造成了某一进程或某

些进程异常情况,那么由于这种制约关系是与一定的活动序列紧密相关的,而这种动作序列

又不易复现。

于是它所造成的进程的异常运行情况也就不易复现。

可见,操作系统外部表现

出来的不确定性就是内部动作序列不可预测、不易复现的反应。

4.进程的结构

在UNIX或者Linux中,进程是通过FORK系统调用被创建的。

在调用了FORK之后,

父进程可以和子进程并行。

父进程还可以创建多个子进程,也就是说,在同一时刻,一个父

进程可以有多个正在运行的子进程。

子进程也可以执行FORK调用。

这样就可以在系统中

生成一个进程树。

进程通常由三部分组成。

一部分是程序,一部分数据集合,另一部分被称为进程控制块

(ProcessControlBlock,简记PCB)。

进程的程序部分描述了进程所要完成的功能。

数据集合部分则有两方面的内容,即程序

运行时所需要的数据部分和工作区。

如果一个程序能为多个进程同时共享执行,它是进程执

行时不可修改的部分。

而数据集合部分则通常为一个进程独占,为进程的可修改部分。

程序

和数据集合是进程存在的物质基础,是进程的实体。

进程控制块有时也称为进程描述块,它包含了进程的描述信息和控制信息,是进程动态

特性的集中反映。

它所包含的信息类型和数量随操作系统而异。

在小型的比较简单的操作系

统中,PCB只占用十几个单元,而在比较复杂的大型操作系统中,PCB则可能占用数十甚

至数百个单元。

但是不管哪一种情况,PCB一般都应包含如下信息:

总之,每个进程基本上有自己独立的代码和数据空间,独立的程序计数器等上下文环境,

进程切换的开销是比较大的。

1.1.2线程

进程具备并发性的特点,这种并发性是不同的进程之间反映出来的,不同的进程有不同

进程空间,进程之间的切换消耗比较大。

那么就考虑到引入线程的概念,在进程的内部引入

并发性,一个进程可以创建多个线程,线程之间具备并发性。

不同的线程之间可以共享进程

的地址空间和数据。

一般的讲,线程是一个程序,或者进程内部的一个顺序控制流。

线程本身不能独立运行,

必须在进程中执行,使用进程的地址空间。

每个线程有自己单独的程序计数器。

一个进程内部包含多个顺序控制流,或者并发执行多种运算,就是多线程。

每个程序执行时都会产生一个进程,而每一个进程至少要有一个主线程。

这个线程其实

是进程执行的一条线索(Thread),除了主线程外你还可以给进程增加其它的线程,也即增

加其它的执行线索,由此在某种程度上可以看成是给一个应用程序增加了多任务功能。

当程

序运行后,您可以根据各种条件挂起或运行这些线程,尤其在多CPU的环境中,这些线程

是可以并发或者并行运行的。

多线程就是在一个进程内有多个线程。

从而使一个应用程序有了多任务的功能。

有人会

问:

多进程技术不是也可以实现这一点吗?

但是创建进程的高消耗(每个进程都有独立的数

据和代码空间),进程之间通信的不方便(消息机制),进程切换的时间太长,这些导致了多

线程的提出。

对于单CPU来说(没有开启超线程),在同一时间只能执行一个线程,所以如

果想实现多任务,那么就只能每个进程或线程获得一个时间片,在某个时间片内,只能一个

线程执行,然后按照某种策略换其他线程执行。

由于时间片很短,这样给用户的感觉是同时

有好多线程在执行。

但是线程切换是有代价的,因此如果采用多进程,那么就需要将线程所

隶属的该进程所需要的内存进行切换,这时间代价是很多的。

而线程切换代价就很少,线程

是可以共享内存的。

所以采用多线程在切换上花费的比多进程少得多。

但是,线程切换还是

需要时间消耗的。

所以采用一个拥有两个线程的进程执行所需要的时间比一个线程的进程执

行两次所需要的时间要多一些。

即采用多线程不会提高程序的执行速度,反而会降低速度,

但是对于用户来说,可以减少用户的响应时间。

上述结果只是针对单CPU,如果对于多CPU

或者CPU采用超线程技术的话,采用多线程技术还是会提高程序的执行速度的。

因为单线

程只会映射到一个CPU上,而多线程会映射到多个CPU上,超线程技术本质是多线程硬件

化,所以也会加快程序的执行速度。

总之,进程内的同一类线程可以共享代码和数据空间,每个线程有独立的运行栈和程序

计数器,切换的开销比较小,灵活性高。

在支持超线程和多核的CPU上,多线程能够并发

或者并行执行,可以在同一时间段内完成不同的任务,或者加快程序的执行。

同一进程内的

多个线程,调度比较灵活,可以相互协调和协作共同完成特定任务,

1.2创建多线程

在Java中创建多线程是一件非常简单的事情。

Java定义了一个线程的概念模型,把一

个线程分为三部分:

虚拟CPU(java.lang.Thread类),虚拟CPU执行的代码和数据。

创建一个java.lang.Thread的对象,就意味着创建了一个线程。

一个由main方法开始执

行的Java程序,至少包含一个线程,即主线程。

创建多个Thread的对象,就创建了多个线

程。

Thread类通过其run()方法来完成起任务,方法run()为线程体。

一般在java中有两

种比较典型的构造线程的方法:

1)继承Thread类,重写run()方法;

2)把线程体从Thread

类中独立出来,形成单独的线程目标对象,就是实现Runnable接口及其run()方法。

这两种方法都是通过Thread类的start()方法启动线程的。

JDK5.0提供了创建线程池并执行线程的方法。

1.2.1继承Thread创建线程

继承java.lang.Thread类创建线程是最简单的一种方法,也最直接。

下面创建一个

MyThread1类,继承Thread,重写其run()方法。

并在main()方法中创建多个并发线程。

packagesimplethread;

publicclassMyThread1extendsThread{

publicMyThread1(Stringname){

super(name);

//传递线程的名字

}

publicstaticvoidmain(String[]args){

//TODOAuto-generatedmethodstub

for(inti=0;

i<

5;

i++){//创建5个线程

newMyThread1("

thread"

+i).start();

}

@Override

publicvoidrun(){

20;

i++){//输出线程名字和i

System.out.println(this.getName()+"

:

"

+i);

这种创建方式,把线程执行的逻辑代码直接写在了Thread的子类中,这样根据线程的

概念模型,虚拟CPU和代码混合在一起了。

并且java是单继承机制,线程体继承Thread

类后,就不能继承其他类了,线程的扩展受影响。

1.2.2实现Runnable接口创建线程

为了构建结构清晰线程程序,可以把代码独立出来形成线程目标对象,然后传给Thread

对象。

通常,实现Runnable接口的类创建的对象,称作线程的目标对象。

下面创建一个类

MyThread2实现Runnable接口,然后创建线程目标对象,传递给虚拟的CPU。

publicclassMyThreadTargetimplementsRunnable{

i++){

//创建线程目标对象

Runnabler=newMyThreadTarget();

//把目标对象传递个Thread,即虚拟的cpu

newThread(r,"

System.out.println(Thread.currentThread().getName()+"

+i);

从程序中可以看出线程目标对象和Thread分开了,并传递给了Thread。

如果有比较复

杂的数据要处理,可以在线程目标对象中引入数据。

使用这种方式获得线程的名字就稍微复

杂一些,需要使用到Thread中的静态方法,获得当前线程对象,然后再调用getName()

方法。

这种方式在较复杂的程序中用的比较普遍。

1.2.3线程池

线程有时称为轻量级进程。

与进程一样,它们拥有通过程序运行的独立的并发路径,并

且每个线程都有自己的程序计数器,称为堆栈和本地变量。

然而,线程存在于进程中,它们

与同一进程内的其他线程共享内存、文件句柄以及每进程状态。

一个进程中的线程是在同一个地址空间中执行的,所以多个线程可以同时访问相同对

象,并且它们从同一堆栈中分配对象。

创建线程会使用相当一部分内存,其中包括有堆栈,以及每线程数据结构。

如果创建过

多线程,其中每个线程都将占用一些CPU时间,结果将使用许多内存来支持大量线程,

每个线程都运行得很慢。

这样就无法很好地使用计算资源。

Java自从5.0以来,提供了线程池。

线程的目标执行对象可以共享线程池中有限书目的

线程对象。

一般的服务器都需要线程池,比如Web、FTP等服务器,不过它们一般都自己实现了线

程池,比如Tomcat、Resin和Jetty等,现在JDK本身提供了,我们就没有必要重复造车轮

了,直接使用就可以,何况使用也很方便,性能也非常高。

下面是使用线程池创建的多线程程序,100个线程目标对象共享2个线程。

packagepool;

importjava.util.concurrent.ExecutorService;

importjava.util.concurrent.Executors;

publicclassTestThreadPool{

publicstaticvoidmain(Stringargs[])throwsInterruptedException{

//在线程池中创建2个线程

ExecutorServiceexec=Executors.newFixedThreadPool

(2);

//创建100个线程目标对象

for(intindex=0;

index<

100;

index++){

Runnablerun=newRunner(index);

//执行线程目标对象

exec.execute(run);

//shutdown

exec.shutdown();

//线程目标对象

classRunnerimplementsRu

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

当前位置:首页 > 表格模板 > 表格类模板

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

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