论文部分内容阅读
【摘要】本文首先介绍了线程的有关概念,接着介绍了线程的生命期及其状态间的转换,线程的优先级与调度策略,从而论述了多线程的安全性以及如何避免线程的死锁,同时本文还论述了多线程的优缺点与如何合理使用。
【关键词】Java;多线程;线程调度;死锁;线程安全;类
1.引言
什么叫多线程?多线程是根据多任务的原理,在一个程序内部实现多个任务的并发执行。其中的每个任务被称为线程(Thread),线程是一个程序内部的顺序控制流。在网络编程中,很多功能是可以并发执行的,比如网络传输速度较慢、用户输入速度较慢,可以用两个独立的线程去完成这两个功能,而不影响正常的显示或其他功能。
Java语言的最大特色之一就是在语言的级别支持多线程,多线程编程技术是java平台的一个重要的技术优势,而且它是第一个直接把跨平台线程模型和正规的内存模型集成到语言中的主流语言。核心类库包含一个Thread类和Runnable接口,可以用它们来构建、启动和操纵线程,其中Thread类是负责向其他类提供线程支持的最主要的类,要使一个类具有线程功能,在java中只有简单地从Thread类派生一个子类就成为一个扩展Thread类。Java语言包括了跨线程传达并发性约束的构造——synchronized和volatile,在简化与平台无关的并发类的开发的同时,它绝没有使并发类的编写工作变得更繁琐,反而使它变得更容易了。因此,在Java中使用多线程相对于在c/c++当中使用多线程来说更加简单与快捷。基本上所有的java程序,包括J2SE,J2EE,J2ME程序都使用了多线程技术。在传统单核、单进程CPU上,Java多线程程序在性能上无法与c++单进程程序相比。但是,随着多核、超进程CPU时代的到来,未来CPU上将能够同时运行更多的线程。
2.线程的生命周期与状态转换
线程同进程一样,是一个动态的概念,所以线程同进程一样,也有一个产生到消亡的生命周期,在其生命周期中,程序可以对线程进行各种控制操作。一个新建的线程的生命期中有如下5中状态。
(1)新建态
当线程用new运算符创建但还未执行的这段时间里,它处于新建状态中,此时,线程对象已被分配了相应的内存空间并被初始化。
(2)就绪状态或可运行状态
当新建的线程启动方法后,就会由新建态进入可运行态。可运行态的线程进入线程队列排队等待CPU资源,随时可以被调度执行。至于可运行态的线程何时才被真正执行,则取决于线程的优先级和排队队列的当前状态。
(3)运行态
当可运行的线程被CPU调度后,就进入运行态,运行态表明线程正在运行,即线程对象正在调用本对象的运行方法。
(4)挂起态
一个正在运行的线程遇到某些特殊情况时,将会进入挂起态。如果一个线程处于挂起状态,这个线程暂时无法进入排队队列。
(5)消亡态
表示线程已经退出运行状态,不再具有继续运行的能力。线程消亡的原因以后两种:一时线程正常运行终止;二是非正常消亡。
3.线程的优先级与调度策略
线程调度的意义在于Java虚拟机(JVM)应对处于可执行状态的多个线程进行系统级的协调,以避免多个线程争用有限资源而导致应用系统死机或者崩溃。为了将线程对于操作系统和用户的重要性区别开来,Java定义了线程的优先级控制和调度策略。
Java将线程的优先级分为10个等级,分别用1-10之间的数字表示。数字越大表明线程的级别越高。相应地,在Thread类中定义了表示线程最低,最高和普通优先级的静态成员变量MIN_PRIORITY,MAX_PRIORITY和NORMAL_PRIORITY,代表的线程优先级等级分别为1,10和5。当一个线程对象别创建时,其默认的线程优先级是5。新建线程将继承创建它的父线程的优先级。父线程是执行创建新线程的语句所在的线程,它可以是程序的主线程,也可以是另一个用户自定义的线程。一般情况下,主线程具有普通优先级。
Java提供一个线程调度器来监控启动后进入就绪状态的所有线程。线程调度器根据线程的优先级决定调度线程的先后顺序,优先级高的线程先调度,优先级低的线程后调度。同时,线程的调度又是抢先式的,如果在当前线程的执行过程中,一个具有更高优先级的线程进入就绪状态,则这个高优先级的线程将立即被调度执行。
4.线程的安全
线程的安全,是指变量或方法可以在多线程的环境下被安全有效的访问。这说明了两方面的问题:1)可以从多个线程中调用,无需调用方有任何操作;2)可以同时被多个线程调用,无需线程不必要的交互。事实上,线程安全是个很难达到的目标,这可以从类的状态上反映出来,归纳的说,类可分为五种状态:
(1)不可变:一个不可变的对象只要构建正确,其外部可见状态永远不会改变,永远也不会看到它处于不一致的状态。Java类库中大多数基本数值类如Integer、String和BigInteger都是原子性的,是不可变的,但Long和Double就不能保证其操作的原子性,可在声明变量的时候用volatile关键字。不可变对象上没有副作用,并且缓存不可变对象的引用总是安全的。一个不可变的对象的一个引用可以自由共享,而不用担心被引用的对象要被修改。
(2)线程安全性类:即对这个类的对象操作序列(读或写其公有字段以及调用其公有方法)都不会使该对象处于无效状态,即任何操作都不会违反该类的任何不可变量、前置条件或者后置条件。
(3)有条件的线程安全类:有条件的线程安全类对于单独的操作可以是线程安全的,但是某些操作序列可能需要外部同步。为了保证其它线程不会在遍历的时候改变集合,进行迭代的线程应该确保它是独占性地访问集合以实现遍历的完整性。通常,独占性的访问是由对锁的同步机制保证的。
(4)线程兼容类:线程兼容类不是线程安全的,但可以通过正确使用同步从而在并发环境中安全地使用。或用一个synchronized块包含每一个方法调用。
(5)线程对立类:线程对立类是那些不管是否调用了外部同步都不能在并发使用时保证其安全的类。线程对立类很少见,当类修改静态数据,而静态数据会影响在其它线程中执行的其它类的行为时,通常会出现线程对立。
5.实现多线程时避免死锁
由于系统资源有限,程序中的多个程序互相等待对方资源,而在得到对方资源前不会释放自己的资源,造成都想得到资源而又都得不到资源,从而导致线程不能继续执行,这就是死锁问题。线程的死锁很少发生,但一旦发生就很难调试。应尽量避免死锁的发生。为了防止死锁现象的发生,在多线程程序设计时可遵循如下原则:
(1)尽量少采用多线程来进行程序设计
(2)在对象的同步方法中调用其他的同步方法要小心
(3)在synchronized封装的块中的执行时间尽可能不要放在synchronized封装的同步块中。
6.多线程的优缺点与合理使用
多线程的最大优点是提供了更好的性能,线程中的处理程序依然是顺序执行,符合普通人的思维习惯。但是多线程的缺点也同样明显,线程的滥用会给系统带来上下文切换的额外负担。并且在线程间共享变量可能造成死锁。合理使用线程和异步,但需要执行I/O操作时,使用异步操作比使用线程和同步I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写、还包括数据库操作等跨进程的调用。
多线程则适用于那种需要长时间CPU运算的场合,例如比较耗时的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所有很多时间往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤(下转第45页)(上接第36页)
大雅,如果需要处理大量的并发操作时就不合适了。
7.结束语
多线程在计算机应用中具有非常积极的意义和广阔的应用前景。利用Java的多线程特性可方便地编写高效率、安全的多线程应用程序。多线程编程增强了程序的交互性,降低了编写复杂任务处理程序的难度,改善了多处理器的性能并增大了数据的吞吐量。当然Java在实现多线程时也有缺点:如对线程的管理会增加CPU的额外负担等。
参考文献
[1]薛为民,夏文红,解仑编著.Java应用教程[M].北京:清华大学出版社,2005,9.
[2]宋中山,严千钧等编著.Java程序设计[M].北京:清华大学出版社,2005,8.
[3]马海军.Java编程原理与实践(第四版)[M].北京:清华大学出版社,2005,8.
[4]洪维恩.Java2面向对象程序设计[M].北京:中国铁道出版社,2005,1.
作者简介:陈海明(1968—),男,大学本科,高级讲师,现供职于福建省漳浦职业技术学校。
【关键词】Java;多线程;线程调度;死锁;线程安全;类
1.引言
什么叫多线程?多线程是根据多任务的原理,在一个程序内部实现多个任务的并发执行。其中的每个任务被称为线程(Thread),线程是一个程序内部的顺序控制流。在网络编程中,很多功能是可以并发执行的,比如网络传输速度较慢、用户输入速度较慢,可以用两个独立的线程去完成这两个功能,而不影响正常的显示或其他功能。
Java语言的最大特色之一就是在语言的级别支持多线程,多线程编程技术是java平台的一个重要的技术优势,而且它是第一个直接把跨平台线程模型和正规的内存模型集成到语言中的主流语言。核心类库包含一个Thread类和Runnable接口,可以用它们来构建、启动和操纵线程,其中Thread类是负责向其他类提供线程支持的最主要的类,要使一个类具有线程功能,在java中只有简单地从Thread类派生一个子类就成为一个扩展Thread类。Java语言包括了跨线程传达并发性约束的构造——synchronized和volatile,在简化与平台无关的并发类的开发的同时,它绝没有使并发类的编写工作变得更繁琐,反而使它变得更容易了。因此,在Java中使用多线程相对于在c/c++当中使用多线程来说更加简单与快捷。基本上所有的java程序,包括J2SE,J2EE,J2ME程序都使用了多线程技术。在传统单核、单进程CPU上,Java多线程程序在性能上无法与c++单进程程序相比。但是,随着多核、超进程CPU时代的到来,未来CPU上将能够同时运行更多的线程。
2.线程的生命周期与状态转换
线程同进程一样,是一个动态的概念,所以线程同进程一样,也有一个产生到消亡的生命周期,在其生命周期中,程序可以对线程进行各种控制操作。一个新建的线程的生命期中有如下5中状态。
(1)新建态
当线程用new运算符创建但还未执行的这段时间里,它处于新建状态中,此时,线程对象已被分配了相应的内存空间并被初始化。
(2)就绪状态或可运行状态
当新建的线程启动方法后,就会由新建态进入可运行态。可运行态的线程进入线程队列排队等待CPU资源,随时可以被调度执行。至于可运行态的线程何时才被真正执行,则取决于线程的优先级和排队队列的当前状态。
(3)运行态
当可运行的线程被CPU调度后,就进入运行态,运行态表明线程正在运行,即线程对象正在调用本对象的运行方法。
(4)挂起态
一个正在运行的线程遇到某些特殊情况时,将会进入挂起态。如果一个线程处于挂起状态,这个线程暂时无法进入排队队列。
(5)消亡态
表示线程已经退出运行状态,不再具有继续运行的能力。线程消亡的原因以后两种:一时线程正常运行终止;二是非正常消亡。
3.线程的优先级与调度策略
线程调度的意义在于Java虚拟机(JVM)应对处于可执行状态的多个线程进行系统级的协调,以避免多个线程争用有限资源而导致应用系统死机或者崩溃。为了将线程对于操作系统和用户的重要性区别开来,Java定义了线程的优先级控制和调度策略。
Java将线程的优先级分为10个等级,分别用1-10之间的数字表示。数字越大表明线程的级别越高。相应地,在Thread类中定义了表示线程最低,最高和普通优先级的静态成员变量MIN_PRIORITY,MAX_PRIORITY和NORMAL_PRIORITY,代表的线程优先级等级分别为1,10和5。当一个线程对象别创建时,其默认的线程优先级是5。新建线程将继承创建它的父线程的优先级。父线程是执行创建新线程的语句所在的线程,它可以是程序的主线程,也可以是另一个用户自定义的线程。一般情况下,主线程具有普通优先级。
Java提供一个线程调度器来监控启动后进入就绪状态的所有线程。线程调度器根据线程的优先级决定调度线程的先后顺序,优先级高的线程先调度,优先级低的线程后调度。同时,线程的调度又是抢先式的,如果在当前线程的执行过程中,一个具有更高优先级的线程进入就绪状态,则这个高优先级的线程将立即被调度执行。
4.线程的安全
线程的安全,是指变量或方法可以在多线程的环境下被安全有效的访问。这说明了两方面的问题:1)可以从多个线程中调用,无需调用方有任何操作;2)可以同时被多个线程调用,无需线程不必要的交互。事实上,线程安全是个很难达到的目标,这可以从类的状态上反映出来,归纳的说,类可分为五种状态:
(1)不可变:一个不可变的对象只要构建正确,其外部可见状态永远不会改变,永远也不会看到它处于不一致的状态。Java类库中大多数基本数值类如Integer、String和BigInteger都是原子性的,是不可变的,但Long和Double就不能保证其操作的原子性,可在声明变量的时候用volatile关键字。不可变对象上没有副作用,并且缓存不可变对象的引用总是安全的。一个不可变的对象的一个引用可以自由共享,而不用担心被引用的对象要被修改。
(2)线程安全性类:即对这个类的对象操作序列(读或写其公有字段以及调用其公有方法)都不会使该对象处于无效状态,即任何操作都不会违反该类的任何不可变量、前置条件或者后置条件。
(3)有条件的线程安全类:有条件的线程安全类对于单独的操作可以是线程安全的,但是某些操作序列可能需要外部同步。为了保证其它线程不会在遍历的时候改变集合,进行迭代的线程应该确保它是独占性地访问集合以实现遍历的完整性。通常,独占性的访问是由对锁的同步机制保证的。
(4)线程兼容类:线程兼容类不是线程安全的,但可以通过正确使用同步从而在并发环境中安全地使用。或用一个synchronized块包含每一个方法调用。
(5)线程对立类:线程对立类是那些不管是否调用了外部同步都不能在并发使用时保证其安全的类。线程对立类很少见,当类修改静态数据,而静态数据会影响在其它线程中执行的其它类的行为时,通常会出现线程对立。
5.实现多线程时避免死锁
由于系统资源有限,程序中的多个程序互相等待对方资源,而在得到对方资源前不会释放自己的资源,造成都想得到资源而又都得不到资源,从而导致线程不能继续执行,这就是死锁问题。线程的死锁很少发生,但一旦发生就很难调试。应尽量避免死锁的发生。为了防止死锁现象的发生,在多线程程序设计时可遵循如下原则:
(1)尽量少采用多线程来进行程序设计
(2)在对象的同步方法中调用其他的同步方法要小心
(3)在synchronized封装的块中的执行时间尽可能不要放在synchronized封装的同步块中。
6.多线程的优缺点与合理使用
多线程的最大优点是提供了更好的性能,线程中的处理程序依然是顺序执行,符合普通人的思维习惯。但是多线程的缺点也同样明显,线程的滥用会给系统带来上下文切换的额外负担。并且在线程间共享变量可能造成死锁。合理使用线程和异步,但需要执行I/O操作时,使用异步操作比使用线程和同步I/O操作更合适。I/O操作不仅包括了直接的文件、网络的读写、还包括数据库操作等跨进程的调用。
多线程则适用于那种需要长时间CPU运算的场合,例如比较耗时的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所有很多时间往往会使用线程来执行耗时较长的I/O操作。这样在只有少数几个并发操作的时候还无伤(下转第45页)(上接第36页)
大雅,如果需要处理大量的并发操作时就不合适了。
7.结束语
多线程在计算机应用中具有非常积极的意义和广阔的应用前景。利用Java的多线程特性可方便地编写高效率、安全的多线程应用程序。多线程编程增强了程序的交互性,降低了编写复杂任务处理程序的难度,改善了多处理器的性能并增大了数据的吞吐量。当然Java在实现多线程时也有缺点:如对线程的管理会增加CPU的额外负担等。
参考文献
[1]薛为民,夏文红,解仑编著.Java应用教程[M].北京:清华大学出版社,2005,9.
[2]宋中山,严千钧等编著.Java程序设计[M].北京:清华大学出版社,2005,8.
[3]马海军.Java编程原理与实践(第四版)[M].北京:清华大学出版社,2005,8.
[4]洪维恩.Java2面向对象程序设计[M].北京:中国铁道出版社,2005,1.
作者简介:陈海明(1968—),男,大学本科,高级讲师,现供职于福建省漳浦职业技术学校。