Java线程实例讲解【原创】
- 格式:doc
- 大小:222.50 KB
- 文档页数:12
深入Java 线程
王伟2009-07-30 一、什么是线程
为了清楚什么是线程,先了解一下程序和进程的概念。
程序就是源代码和文档的集合,如果经过编译后,就是目标平台可执行的指令集合。
程序是静态的。
相比之下,进程是动态的。
操作系统加载可执行程序后,即产生了一系统进程,由操作系统调度管理。
现代操作系统都是多任务多用户的,意味着操作系统可以调度许多进程。
进程之间亦可以依赖于操作系统进行通信。
进程是重量级的,因为进程的调度需要耗费大量的CPU和内存资源。
线程是进程的进一步划分,可以认为一个进程由多个线程按各自的执行路线运行而组成。
由于线程共享同一个进程内存空间,线程间通信、调度开销要比进程之间的通信、调度开销要小得多。
因此,现在的应用程序往往会采用多线程编程来提高性能。
二、创建Java线程
Java提供了语言级对线程的支持。
Java中线程被抽象为对象,线程的创建需要用到Thread类或者Runnable接口。
也就是说线程类的编写有两种方式:一种是扩展Thread类;另一种是实现Runnable接口。
无论是哪一种方式,线程的执行体都是run方法。
代码Thread_1,采用扩展Thread类的方式定义线程类
测试类:
代码Thread_2,采用实现Runnable接口的方式定义线程类
测试类:
三、Java线程的状态及常用方法
Java线程状态可以用下表描述:
以下是线程状态过渡示意图:
Thread.start( ) 方法通知线程规化器这个线程已准备就绪,并尽快执行线程,即执行线程类的run方法。
这意味着编程只能控制线程进入准备运行状态,何时真正运行则由线程规化器决定。
Thread.sleep( ) 方法将当前线程置入睡眠状态,进入睡眠状态的线程可能被其他线程中断而采取某种行为。
睡眠线程被中断时,sleep方法抛出一InterruptException异常。
代码Thread_3
测试类:
输出结果:
在主线程(main方法)中,使用t3.interrupt( )中断了正在休眠的线程t3。
实际上,Thread.interrupt( ) 方法只是在目标线程中设置了一个标志,表示它已经被中断,并立即返回。
上例中,Thread.sleep( ) 方法检测到t3被中断,因此中断t3的休眠状态并抛出InterruptionException。
如果在调用sleep()之前线程已经被interrupt ( )方法标志为中断,sleep( ) 会立即抛出InterruptionException异常。
代码Thread_4:
main( ) 方法为主线程,Thread.currentThread( ) 方法能够获得当前线程的引用,即主线程。
在主线程休眠之前,主线程被标识为中断,因此sleep( ) 方法立即抛出InterruptionException异常。
再来看一个例子:
测试类代码:
创建了2个线程t1、t2,t1的优先级为最低,而t2则是最高优先级,如果在线程类Thread_5中没有代码:
线程t1,几乎没有机会获得处理器来执行代码,出于等待状态,加上次代码后,如果是t2获得了处理器,执行完输出后,会通过Thread.yeild( ) 方法主动放弃使用处理器的权利,从而使得t1有机会执行。
对于Thread.stop( ) 、Thread.suspend( ) 、Thread.resume( ) 从Java 1.2之后不再赞成使用,这里不予以介绍。
四、线程并发
先看到一个例子:
代码Thread_6:
代码SharedObject:
测试代码:
测试代码输出:
从输出我们首先可以发现,doWork方法被X线程调用的同时也被Y线程调用。
其次我们发现输出结果不正确,每个线程对value累加3次,value 最终结果应该是7,而不是6。
Thread.join( ) 方法的作用为等待线程结束,测试代码中,主线程输出value的结果,需要在线程X、Y都结束的情况下输出。
从上例我们可以得出结论,如果存在多线程线程并发的情况,数据很容易出现不一致的情况。
Java多线程编程提供了两种机制来解决数据不一致的问题,分别需要使用到关键字volatile和synchronized。
待续
附录、有关线程的JVM知识
⏹什么是JVM
对Java虚拟机的理解,可以从以下三个方面着手:
Sun制定的抽象规范
一个具体的实现,可以是软件实现,也可以是硬件实现,甚至是软硬件混合实现
一个运行中的虚拟机实例
大多数时候,JVM的实现是基于操作系统之上的软件实现,比如Sun实现的JRE、BEA的JRockit等。
当运行一个Java程序时,也就运行了一个JVM 实例;当这个程序结束时,该虚拟机实例也随之消亡。
对于操作系统来说,JVM即是一个进程,而Java程序仅仅是其中的一个线程,这个线程的入口即是Java程序中的main方法。
GC(垃圾收集器)也是JVM进程中的一个线程。
GC是JVM控制的,Java程序对其的控制非常有限。
如果在Java程序中创建了新的线程,那么这个线程的入口为ng.Thread或者ng.Runnable中的run方法。
⏹JVM的体系结构
在JVM规范中,使用子系统、内存区、数据类型及指令几个部分对JVM 进行描述。
JVM规范通过定义这些组成部分以及它们之间的交互来定义任何JVM实现都必须遵守的行为。
下图为JVM内部体系结构:
当JVM运行一个Java程序时,它需要内存来存储许多内容,例如,字节码、对象、参数、返回值、局部变量以及运行计算的中间结果等。
JVM把
这些内容组织到称为“运行时数据区”的内存中。
其中方法区、堆是JVM中所有线程共享的。
当每创建一个新线程时,该线程都得到它自己的PC寄存器以及一个Java栈。
它的PC寄存器存放该线程的下一条非本地指令地址,Java 栈保存该线程执行的非本地方法的状态——局部变量、参数、返回值等。
本地方法(native method)的状态保存在本地方法栈中。
下图描述了线程专有的数据区:。