APP下载

Java同步线程模型分析与改进*

2015-05-04

武汉交通职业学院学报 2015年2期
关键词:线程进程内存

黄 庆

(武汉交通职业学院,湖北 武汉 430065)

Java同步线程模型分析与改进*

黄 庆

(武汉交通职业学院,湖北 武汉 430065)

Java是为数不多的提供内置多线程机制的编程语言。Java多线程程序虽然能够提高计算机资源的使用效率及处理速度,但其同步多线程模型还存在一定的缺陷。特别是多线程应用系统程序设计时,如果算法和策略不当,反而会引起死锁等问题。文章分析Java同步多线程模型的优缺点,并对Java多线程应用系统的死锁问题提出改进策略。

Java线程模型;同步;死锁;改进

很多编程语言,如C语言和C++语言都不提供内置的多线程机制,要实现并发处理,需要调用操作系统的多线程原语操作。Java类库中本身包含多线程原语操作,编写应用程序时可以直接使用内置的多线程操作以实现并发处理。多线程程序能够有效提高计算机资源的使用效率,提高处理速度。但Java多线程程序在并行处理的同时,也会产生死锁等问题,特别是对于具有“生产者/消费者”之类的并发处理程序。Java防止产生死锁的方法是采用线程同步操作。

1 Java同步线程模型的分析

1.1 Java引入同步线程的优点

引入同步线程模型,系统并发执行程度可以大大提高,编程中并发程序产生的问题与局限性能得到有效的控制。这种线程概念的应用,和多进程模式相比较,Java同步线程模型有更多优点。二者的区别在于,进程的两个单位分开。线程中,每个执行单元,既可以独立调度,又可以各自分派。每个进程只是一个独立出来的控制流,而非可独立拥有资源的基本单位。因此,操作时可以减少对独立拥有资源的单位做频率较高的切换,程序并发执行程度的功能也因此得到较大的突破。

可拥有资源的独立单位和可独立调度及分派的基本单位,是进程并发执行的必要条件,这两种属性在传统的操作系统也得到体现。进程的这两种属性,为进程并发执行起到关键作用。但是,为使程序并发得到有效执行,创建、撤销等操作必须在系统内实施,因为作为资源的拥有者,不同进程所拥有的内存空间堆栈不同,所以系统在进行这些操作过程中,一定要给其腾出更大的时空。基于此,系统中所设置的进程数目不应该过多,进程在切换的次数也不宜太高,从这点看来,对并发进程的进一步发展是一种限制。而值得注意的是,Java同步线程模式,难点在于攻克并发程序设计中出现的各种问题,系统中内置多个进程或线程,并发执行的进程和线程又是各自独立,必会导致相互间出现资源共享关系、相互协作关系。

Java作为多线程模式语言,具有同时实行多个线程和实施多个操作功能,并且多线程技术还可以增强图形用户界面之间的交互能力,当前的主流操作系统如Windows等,都采用了这种线程的概念,即把线程视为基本执行单位。在普通程序中,仅有一个执行程序,许多情况没法或者极难处理。例如,在GUI应用程序中,一般都希望在进行后台计算的同时能响应用户输入。因此,应用程序在执行时要实行中断计算,检测用户输入的状况,这样必然存在危险,而Java的多线程操作可避免这种情况。

1.2 Java内存模型对同步多线程的支持

作为平台无关的编程语言,Java语言规范定义了一个统一的内存管理模型(JMM),[1]具体由Java虚拟机(JVM)来实现。JMM将线程能访问的内存划分为主内存(main memory)和工作内存(working memory)。所有的线程都能对主内存中的数据进行并发访问。而每一个线程都有其私有的其他线程不能访问的工作内存,其中存放的是该线程从主内存中拷贝过来的变量以及访问方法所取得的局部变量。每个线程对变量的操作,都是先从主内存将其拷贝到工作内存,再对其进行操作,各线程之间想要读取或修改对方所访问的变量均需要通过主内存。当需要修改变量值时,先从主内存复制到工作内存,修改其值后将其拷贝回主内存。

多线程并发执行时,在资源上形成共享与合作关系。由于进程线程之间相互协作,需要共同完成目标操作。但是,如果线程和进程的协作关系出现了矛盾,结果将不堪设想,会给整个系统带来危险。比如,对具有“生产者/消费者”之类的并发处理程序,Java多线程程序在并行处理的同时,也会产生死锁等问题。所谓死锁,即当两个或多个线程彼此无限期等待,导致都不能继续执行的状态。Java同步线程解决这一问题的方法是使用synchronized关键字,对该对象完成锁操作,并且在完成锁操作前停止处理。锁操作完成时,synchronized语句体得到执行;当语句体执行完毕,无论正常或异常,解锁操作自动完成。如程序add Number 和print方法均是synchronized方法,Java就会自动地在一个线程执行synchronized方法时,禁止其他线程执行它。只有当所有synchronized方法结束后,等待的线程才能执行。

Java线程访问标识为Synchronized的方法,并对相应变量做操作时,JVM执行过程的描述如图1所示。

图1 JVM的执行过程

synchronized关键字可以保证它锁住的代码区访问的唯一性,即任何时刻只允许唯一的一个线程单独访问同步区。[2]如果不使用synchronized关键字,则JVM就不能保证读取数据和写回数据按上述次序执行,从而可能引起程序数据混乱。

2 Java同步线程模型的缺陷

Java编程语言的线程模型是该语言的薄弱环节,同实际复杂程序的要求并不完全相适应。Java语言本身对线程的语法和类包的支持比较少,只能适用某些小型应用环境。基于这些缺陷,发掘Java多线程更多的实现方法,处理由多线程产生的数据死锁,显得尤为重要。

因为并发线程会竞争程序中的资源,共享资源就必须得到均衡分配,让每线程在程序执行访问过程中,能充分利用有限的资源。由于Java中并不对死锁提供必要的检测,系统自身也没有死锁线程的设置,因此对Java程序员来说,算法上要防止产生死锁。许多线程程序设计可以归纳出死锁产生的条件。例如,两个或两个以上的synchronized方法互相依存,相互之间无限期等待,以至于所有线程均无法继续,导致死锁。如下例:

class Printer implements Runnable {

Input input ;

synchronized void print ( ) {

input . write ( ) ;

}

synchronized void sendData ( ) {

}

public void run ( ) {

print ( ) ;

}

}

class Input implements Runnable {

Printer printer;

synchronized void write ( ) {

printer. sendData ( ) ;

}

public void run ( ) {

write ( ) ;

}

}

class Deadlock {

public static void main ( string args [ ] ) {

Printer P= new Printer ( ) ;

Input I= new Input ( ) ;

P·input= I;

I·printer= P;

Thread t1= new Thread (P) ;

Thread t2= new Thread (I) ;

t1·start ( ) ;

t2·start ( ) ;

}

}

由编程看,每一个对象有一个与其相关的monitor对象。monitor的作用类似于看门人,每次仅允许一个synchronized方法进入,当该synchronized方法结束后,monitor解锁,另一个synchronized方法开始执行。如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的,其中系统均衡是指每线程在执行过程中都能充分访问有限的资源,而且系统中没有死锁的线程。

同步线程中,许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,而在Java中的每个对象都有一把锁与之对应,但Java不提供单独的lock和unlock操作,它由高层的结构隐式实现,来保证操作的对应。

上述编程,运行Printer对象和Input对象的线程执行时会造成死锁,因为这些线程交替执行的方式不正确。具体来讲,Printer对象为执行其printer方法,锁住了它的monitor;随后Input对象为执行其write方法,也锁住了自己的monitor;而后printer方法需调用Input对象的write方法,但Input对象的monitor已经加锁,因此printer方法等待。同时Input对象需加锁printer对象的monitor,以执行printer对象的sendData方法,printer对象的monitor已经加锁,因此Input对象也需等待。这样造成彼此无限期等待,两个线程均不能继续(如图2)。

图2 两线程的“死锁”执行状态

3 改进策略

可采用一些简单的策略和算法解决死锁问题,编制出正确线程程序。首先,完成给定的目标任务,并且和其他任务同时运行,多线程就需要应用在这类编程中。此时,可明确控制每个线程完成的功能,线程同步位置在代码中也就找到确定的位置。在上例中,大可不必应用两个线程去实行操作,因为它们之间关系是互相依赖,当从一个synchronized方法中调用另一个synchronized方法时,要谨慎。因为如果由两个或两个以上的线程能够对这些方法独立实施时,可能导致数据死锁状态。在这种情况下,即使用同步多线程技术,加锁时间也应当缩短到最少。如果目标完成需要较长时间,最好不要加锁。防止死锁的另一种方法是,根据每个线程所需的资源数目,给竞争的资源编号,让它运行时先得到序号较小的资源,然后再进行大序号资源的争夺。[3]

4 结语

Java语言编程技术越来越成熟,其同步线程模型改进也将逐步完善。本文通过对Java语言内置多线程机制和对引入的有效同步机制的优点和缺陷进行具体论述,来探讨解决多线程序并发程序异常问题的解决方法。相信随着新科技的发展,Java同步线程模型将会有突破性的发展。

[1]周志远,张大方,缪力.基于Java内存模型的并发程序模型检测[J].计算机工程与科学,2010,(3):111-123.

[2]胡雯,赵海廷.JAVA多线程同步问题研究[J].软件导刊,2007,(1):98-99.

[3]于利前,王林章,雷斌,等.静动态结合的Java程序不变性分析方法[J].计算机学报,2010,(4):736-745.

2015-03-12

黄 庆(1977-),女,湖北武汉人,武汉交通职业学院电子与信息工程学院讲师,主要从事计算机程序设计、项目开发的教学与研究。

10.3969/j.issn.1672-9846.2015.02.022

TP311.10

A

1672-9846(2015)02-0084-03

猜你喜欢

线程进程内存
基于C#线程实验探究
基于国产化环境的线程池模型研究与实现
债券市场对外开放的进程与展望
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
改革开放进程中的国际收支统计
浅谈linux多线程协作
内存搭配DDR4、DDR3L还是DDR3?
社会进程中的新闻学探寻
俄罗斯现代化进程的阻碍