admin 管理员组

文章数量: 1184232


2024年3月19日发(作者:jsswitch编程入门书籍)

应用

il一一

科学

Java

多线程编程的探究

刘翠焕司海峰李素朵

(石家庄法商职业学院河北石家庄050091)

[摘要】通过对'Java多线稃编程的研究,得出了如何灵活、正确的使用多线程编程提高整个应用系统的性能,同时对在使用多线程编程中容易出现的问题,也

做出了提示,希望能对编程者有所帮助。

【关键词】线程优先级

中圈分类号:TP3

同步阻塞

文章编号;1671--7597(2--02文献标识码:^

线程本是操作系统的一个重要概念。多线程是指程序中同时存在着多

个执行体,他们按几条不同的执行线路共同工作,独立完成各自的功能而

互不干扰。使用多线程技术可以使系统同时运行多个执行体,减少程序的

响应时间,提高计算机资源的使用效率。正确的使用多线程技术可以提高

整个应用系统的性能.而多线程机制也是Java语言的一个重要特征。

一、如何在Java中宴现多线程

在Java中,创建线程有两种方法:一是通过创建Thread二类的子类来实

现,二是通过实现Runnable:接口的类来实现。下面我们就这来那各种方法

展开讨论。

(一)创建!Thread:类的子类

Thread类最重要的方法是run0。它为Thread类的方法start0

所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只

需要覆盖它l

我们在创建的Thread类的子类中重写run(),加入线程所要执行

的代码即可。下面是一个例子:

public

class

MyThread

extendsThread

{

String

threadNeme:

intsecond:

int

times:

public

MyThread(Stringneme,int

x,int

m)

{

threadName=neme:

second=x:

times=m;

public

void

run0

{

try{

for(inti:o:i

{System

out.print

II.threadNeme):

Sleep

ICsecond):}

SysteL

out.println(。线程”+threadNeme+。结束!”):

catch(InterruptedException

e)

.{System.out.println(e):

}

}

public

static

voidmain(String

argsU)

(’Mythreadthreada=new

Mythread(“A”,100,20。J’:

Mythread

threadb=new

Mythread(。B”,200,20):

threada.start

0:

threadh.start

0:

}

这种法简单明了,符合大家的习惯,但是,它也有一个很大的缺

点,那就是如果我们的类已经从一个类继承(如小程序必须继承自Applet

类),则无法再继承Thread类,这时如果我们又不想建立一个新的类,

应该怎么办呢?

我们不妨来探索一种新的方法:我们不创建Thread类的子类,而是

直接使用它,那么我们只能将我们的方法作为参数传递给Thread类的实

例,有点类似回调函数。但是3ava没有指针,我们只能传递一个包含这

个方法的类的实例。那么如何限制这个类必须包含这一方法呢?当然是使

用接口l(虽然抽象类也可满足,但是需要继承。而我们之所以要采用这

种新方法,不就是为了避免继承带来的限制吗?)

(二)通过实现!RunnabIe接口的类

Runnable接口只有一个方法run(),我们声明自己的类实现Runnable

接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的

任务。但是Runnable:接口并没有任何对线程的支持,我们还必须创建

Thread.类的实例,这一点通过Thread类的构造函数public

Thread(

Runnable

target)来实现。下面是一个例子:

public

class坶Thread

implements

Runnable

{int

count:l,

number:

publ

ic

MyThread(int

nul)

number

5

hem;

System。out.println(。创建线程

+

number)]:

}

pubiic

void

run(]}

whi

le(true)

System,out.println(’线程’+number+’:计数’+count):

if(++count=6)return;

}

public

static

void

main(『'。String

args[])

{

for(int=0:

i<

5;

i++)

new

Thread(new

MyThread(i+1)).start

0:

}

}

严格地说,创建!Thread子类的实例也是可行的,但是必须注意的是,

该子类必须没有覆盖1'hread类的run方法。否则该线程执行的将是子类

的run方法,而不是我们用以实现,Runnablej凄口的类的run方法,对此大家

不妨试验一下。

使用Runnable接口来实现多线程使得我们能够在一个类中包容所有的

代码,有利于封装,它的缺点在于.我们只能使用一套代码,若想创建多

个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的

话,在大多数情况下也许还不如直接用多个类分别继承Thread来得紧凑。

:、缀程的优先曩

线程的优先级代表该线程的重要程度.当有多个线程同时处于可执行

状态井等待获得CPU时间时,线程调度系统根各个线程的优先级来决定给

谁分配CPU时间,优先级高的线程有更大的机会获褶LCPU时间,优先级低的

线程也不是没有机会,只是机会要小一些罢了。

翥霎Ⅵ裂器一㈧

可以调用Thread类的方法getPriority0和setPriority()来存取线程

的优先级。线程的优先级界于1(MIN_PRIORITY)和lO(1IAxPRIORITY)之间,

缺省是5(NORM

PRIORITY).

兰、媛程的同步

由于同一进程的多个线程共享同~片存储空间,在带来方便的同时,

也带来了访问冲突这个严重的问题。Java中引入了“互斥锁”的标记,保

证任一时刻只能一个线程访问数据。synchronized关键字与互斥锁联系,

它包括两种用法:synchronized方法和synchronized块。

(--)synchronized方法

通过在方法声明中加入synchronized关键字来声明synchronized

方法。如:public

synchronizedvoid

accessVal(intnewVal):

Synchronized方法控制对类成员变量的访问:每个类实例对应一把

锁,每个synchronized方法都必须获得调用该方法的类实例的锁方能执

行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回

时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

这种机制确保了同一时刻对于每一个类实例,其所有声明为synchronized

的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得

该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有

可能访问类成员变量的方法均被声明为synchronized)。

在Java中,不光是类实例,每一个类也对应一把锁,这样我们也可将

类的静态成员函数声明为synchronized,以控制其对类的静态成员变量的

访问.

synchronized方法的缺陷:若将一个大的方法声明为synchronized

将会大大影响效率,典型地,若将线程类的方法run()声明为synchronized

,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何

synchronized方法的调用都永远不会成功。当然我们可以通过将访问类成

员变量的代码放到专门的方法中,将其声明为synchronized,并在主方法

中调用来解决这一问题,但是Java为我们提供了更好的懈决办法,那就是

synchronized块。

(二)synchronized块

通过synchronized关键字来声明synchronized块。语法如下:

synchronized(syncObject)f//允许访问控制的代码}

synchronized块是这样一个代码块,其中的代码必须获得对象

syncObject(如前所述,可以是类实例或类)的锁方能执行,具体机制同

前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性

较高。

四、线程的阻塞

为了解决对共享存储区的访问冲突,Java引入了同步机制,现在让

我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为

在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻

准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,

Java引入了对阻塞机制的支持。Java提供了大量方法来支持阻塞。

(一)SIeep0方法

sleep0允许指定以毫秒为单位的一段时间作为参数,它使得线程在

指定的时间内进入阻塞状态,不能得到CPU时间,指定的时间一过,线程

重新进入可执行状态。

典型地,sleep()被用在等待某个资源就绪的情形:测试发现条件不

满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

(二)suspend0和reeume0方法

两个方法配套使用,suspend0使得线程进入阻塞状态,并且不会自

动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状

态.典型地,suspend()和resume()被用在等待另一个线程产生的结果的情

形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果

后,调用resume()使其恢复。

(--)Yiefd0方法

Yield()使得线程放弃当前分得的CPU时间,但是不使线程阻塞,即线

程仍处于可执行状态,随时可能再次分得CPU时间.调用yield0的效果等

价于调度程序认为该线程已执行了足够的时间从而转到另一个线程.

(四)wait0和notify0方法

两个方法配套使用,wait0使得线程进入阻塞状态,它有两种形式,

一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者

当对应的notify0被调用或者超出指定时间时线程重新进入可执行状态,

后者则必须对应的notify0被调用.

(1)关于wait0和notify()方法两点说明:第一l调用notify0

方法导致解除阻塞的线程是从因调用该对象的wait0方法而阻塞的线程中

随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小

心,避免因这种不确定性而产生问题。第二:除了notify(),还有一个方

法notifyAll()也可起到类似作用,唯一的区别在于,调用notifyAll()方

法将把因调用该对象的wait0方法而阻塞的所有线程一次性全部解除阻

塞。当然,只有获得锁的那一个线程才能进入可执行状态.

(2)wait0和notify0方法的特点:初看起来它们与suspend()和

resume0方法对没有什么分别,但是事实上它们是截然不同的。区别的核

心在于。前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了

的话),而这一对方法则相反。

首先,前面叙述的所有方法都隶属于Thread类,但是这一对方法却

直接隶属于Object类,也就是说,所有对象都拥有这一对方法。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却

必须在synchronized方法或块中调用。因为只有在synchronized方法或

块中当前线程才占有锁,才有锁可以释放。

wait()和notify()方法的上述特点决定了它们经常和synchronized

方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会

发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的

功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于

block和wakeup原语(这一对方法均声明为synchronized)。它们的结

合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号

量算法),并用于解决各种复杂的线程间通信问题。

suspend0方法和不指定超时期限的wait

0方法的调用都可能产生

死锁。Java并不在语言级别上支持死锁的避免,我们在编程中必须小心地

避免死锁。

五、线程组

线程组是一个Java特有的概念,在Java中,线程组是类ThreadGroup

的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指

定并在线程的整个生命期内都不能更改。你可以通过调用包含ThreadGroup

类型参数的Thread类构造函数来指定线程属的线程组,若没有指定,则线

程缺省地隶属于名为system的系统线程组。

Java允许我们对一个线程组中的所有线程同时进行操作,比如我们可

以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动

或阻塞其中的所有线程。

Java的线程组机制的另一个重要作用是线程安全。线程组机制允许我

们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处

理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java的

ThreadGroup类提供了大量的方法来方便我们对线程组树中的每一个线程

组以及线程组中的每一个线程进行操作。

六、结柬语

多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间

的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是

它的内在特点是否符合多线程的特点。

参考文献:

[1]‘面向对象程序设计Java语言>[M],杜春涛,中国铁道出版社,

2007.8.

[2]‘深入学习;Java多线程编程)[岫,美Bil

lewisDaniel

J

Berg,电子工业出版社.2000.12.

[3]‘深入浅RiJava多线程程序设计'论文百事通网站,2007.II.


本文标签: 线程 方法 执行