- 浏览: 410853 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (347)
- java基础 (58)
- ajax (10)
- s2sh (10)
- 版本控制 (4)
- 数据库 (34)
- 服务器 (4)
- 开发工具 (8)
- javascript (15)
- soockte (5)
- ext (2)
- 环境搭建 (7)
- struts2 (9)
- 找工作中的面试技巧 (2)
- 承接网站零活 (0)
- JNI+JONSE+OGNL (8)
- 性能优化 (4)
- Android开发 (5)
- xul (8)
- jquery (2)
- 线程 (3)
- jsp+jdbc (7)
- servlet (2)
- java对xml操作 (1)
- IO流的操作 (10)
- 项目开发前配置 (1)
- css (0)
- 上传、下载 (2)
- 知识探讨 (2)
- html (2)
- HQL (0)
- 工作技巧 (1)
- IT (1)
- Hibernate杂谈 (10)
- Spring杂谈 (35)
- DWR (5)
- JUnit测试 (3)
- EasyMock测试web (1)
- ibatis (6)
- maysql (5)
- C++ (0)
- 正则表达式(解剖) (1)
- 密码安全 (2)
- 上传 (1)
- socket (1)
- jni(java与c++结合) (1)
- jdk版本问题 (0)
- tomcat版本问题 (5)
- linux基本命令(初学) (7)
- linux项目发布 (1)
- 3年的经验总结 (1)
- 加解密 (2)
- 高级java阶段 (2)
- java内存分区 (1)
- 浏览器 (1)
- 职业规划 (1)
- 管理 (5)
- java语音 (1)
- SSH (1)
- jsp (3)
- extjs (1)
- uml (2)
- 加密 (1)
- web (2)
- Ant (1)
- 自述 (1)
- Linux (1)
- ssh源码解剖 (1)
- 代码优化 (1)
- 设计模式 (0)
- xml (2)
- JOSN (1)
- scala (0)
- hadoop (0)
- spark (0)
- hana (1)
- shior (1)
- java Word (6)
- java PDF (4)
- java Excel (0)
最新评论
-
高级java工程师:
ztao2333 写道谢谢。收藏下这个总结。呵呵
温习jdk和tomcat -
ztao2333:
大写的,不是大学的
温习jdk和tomcat -
ztao2333:
谢谢。收藏下这个总结。
温习jdk和tomcat -
the_small_base_:
你好,可以提供调用方法吗?需要的Jar,能发下源码吗?谢谢
java实现语音 -
高级java工程师:
文思涌动 写道楼主新年好。可否再传一遍给我,我没有收到, 不清 ...
s2sh整合
一、进程与应用程序 的区别
进程(Process )是最初定义在Unix 等多用户、多任务操作系统 环境下用于表示应用程序 在内存环境中基本执行单元的概念。以Unix 操作系统 为例,进程是Unix 操作系统 环境中的基本成分、是系统 资源分配的基本单位。Unix 操作系统 中完成的几乎所有用户管理和资源分配等工作都是通过操作系统 对应用程序 进程的控制来实现的。
C 、C++ 、Java 等语言编写的源程序 经相应的编译器编译成可执行文件 后,提交给计算机处理器运行。这时,处在可执行状态中的应用程序 称为进程。从用户角度来看,进程是应用程序 的一个执行过程。从操作系统 核心角度来看,进程代表的是操作系统 分配的内存、CPU 时间片等资源的基本单位,是为正在运行的程序 提供的运行环境。进程与应用程序 的区别在于应用程序 作为一个静态文件 存储在计算机系统 的硬盘等存储空间中,而进程则是处于动态条件下由操作系统 维护的系统 资源管理实体。多任务环境下应用程序 进程的主要特点包括:
● 进程在执行过程中有内存单元的初始入口点,并且进程存活过程中始终拥有独立的内存地址空间;
● 进程的生存期状态包括创建、就绪、运行、阻塞和死亡等类型;
● 从应用程序 进程在执行过程中向CPU 发出的运行指令形式不同,可以将进程的状态分为用户态和核心态。处于用户态下的进程执行的是应用程序 指令、处于核心态下的应用程序 进程执行的是操作系统 指令。
在Unix 操作系统 启动过程中,系统 自动创建swapper 、init 等系统 进程,用于管理内存资源以及对用户进程进行调度等。在Unix 环境下无论是由操作系统 创建的进程还要由应用程序 执行创建的进程,均拥有唯一的进程标识(PID )。
二、进程与Java 线程的区别
应用程序 在执行过程中存在一个内存空间的初始入口点地址、一个程序 执行过程中的代码执行序列以及用于标识进程结束的内存出口点地址,在进程执行过程中的每一时间点均有唯一的处理器指令与内存单元地址相对应。
Java 语言中定义的线程(Thread )同样包括一个内存入口点地址、一个出口点地址以及能够顺序执行的代码序列。但是进程与线程的重要区别在于线程不能够单独执行,它必须运行在处于活动状态的应用程序 进程中,因此可以定义线程是程序 内部的具有并发性的顺序代码流。
Unix 操作系统 和Microsoft Windows 操作系统 支持多用户、多进程的并发执行,而Java 语言支持应用程序 进程内部的多个执行线程的并发执行。多线程的意义在于一个应用程序 的多个逻辑单元可以并发地执行。但是多线程并不意味着多个用户进程在执行,操作系统 也不把每个线程作为独立的进程来分配独立的系统 资源。进程可以创建其子进程,子进程与父进程拥有不同的可执行代码和数据内存空间。而在用于代表应用程序 的进程中多个线程共享数据内存空间,但保持每个线程拥有独立的执行堆栈和程序 执行上下文(Context )。
基于上述区别,线程也可以称为轻型进程 (Light Weight Process ,LWP) 。不同线程间允许任务协作和数据交换,使得在计算机系统 资源消耗等方面非常廉价。
线程需要操作系统 的支持,不是所有类型的计算机都支持多线程应用程序 。Java 程序 设计语言将线程支持与语言运行环境结合在一起,提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中,将衣服放到洗衣机中自动洗涤后将大米放在电饭锅里,然后开始做菜。等菜做好了,饭熟了同时衣服也洗好了。
需要注意的是:在应用程序 中使用多线程不会增加 CPU 的数据处理能力。只有在多CPU 的计算机或者在网络 计算体系结构下,将Java程序 划分为多个并发执行线程后,同时启动多个线程运行,使不同的线程运行在基于不同处理器的Java 虚拟机中,才能提高应用程序 的执行效率。
另外,如果应用程序 必须等待网络 连接或数据库 连接等数据吞吐速度相对较慢的资源时,多线程应用程序 是非常有利的。基于Internet 的应用程序 有必要是多线程类型的,例如,当开发要支持大量客户机的服务器 端应用程序 时,可以将应用程序 创建成多线程形式来响应客户端的连接请求,使每个连接用户独占一个客户端连接线程。这样,用户感觉服务器 只为连接用户自己服务,从而缩短了服务器 的客户端响应时间。
三、Java 语言的多线程程序 设计方法
利用Java 语言实现多线程应用程序 的方法很简单。根据多线程应用程序 继承或实现对象的不同可以采用两种方式:一种是应用程序 的并发运行对象直接继承Java 的线程类Thread ;另外一种方式是定义并发执行对象实现Runnable 接口。
继承Thread 类的多线程程序 设计方法
Thread 类是JDK 中定义的用于控制线程对象的类,在该类中封装了用于进行线程控制的方法。见下面的示例代码:
从上面的程序 代码可以看出:多线程执行地下Consumer 继承Java 语言中的线程类Thread 并且在main 方法中创建了三个Consumer 对象的实例。当调用对象实例的start 方法时,自动调用Consumer 类中定义的run 方法启动对象线程运行。线程运行的结果是每间隔nTime 时间打印出对象实例中的字符 串成员变量strConsumer 的内容。
可以总结出继承Thread 类的多线程程序 设计方法是使应用程序 类继承Thread 类并且在该类的run 方法中实现并发性处理过程。
实现Runnable 接口的多线程程序 设计方法
Java 语言中提供的另外一种实现多线程应用程序 的方法是多线程对象实现Runnable 接口并且在该类中定义用于启动线程的run 方法。这种定义方式的好处在于多线程应用对象可以继承其它对象而不是必须继承Thread 类,从而能够增加类定义的逻辑性。
实现Runnable 接口的多线程应用程序 框架代码如下所示:
//Consumer.java
import java .util.*;
class Consumer implements Runnable
{
… …
public Consumer(int nTime, String strConsumer){… …}
public void run(){… …}
static public void main(String args[])
{
Thread aConsumer = new Thread(new Consumer(1000, "aConsumer"));
aConsumer.start();
// 其它对象实例的运行线程
//… …
}
}
从上述代码可以看出:该类实现了Runnable 接口并且在该类中定义了run 方法。这种多线程应用程序 的实现方式与继承Thread 类的多线程应用程序 的重要区别在于启动多线程对象的方法设计方法不同。在上述代码中,通过创建Thread 对象实例并且将应用对象作为创建Thread 类实例的参数。
四、线程间的同步
Java 应用程序 的多个线程共享同一进程的数据资源,多个用户线程在并发运行过程中可能同时访问具有敏感性的内容。在Java 中定义了线程同步的概念,实现对共享资源的一致性维护。下面以笔者最近开发的移动通信计费系统 中线程间同步控制方法,说明Java 语言中多线程同步方式的实现过程。
在没有多线程同步控制策略条件下的客户账户类定义框架代码如下所示:
public class RegisterAccount
{
float fBalance;
// 客户缴费方法
public void deposit(float fFees){ fBalance += fFees; }
// 通话计费方法
public void withdraw(float fFees){ fBalance -= fFees; }
… …
}
读者也许会认为:上述程序 代码完全能够满足计费系统 实际的需要。确实,在单线程环境下该程序 确实是可靠的。但是,多进程并发运行的情况是怎样的呢?假设发生这种情况:客户在客户服务中心进行缴费的同时正在利用移动通信设备仅此通话,客户通话结束时计费系统 启动计费进程,而同时服务中心的工作人员也提交缴费进程运行。读者可以看到如果发生这种情况,对客户账户的处理是不严肃的。
如何解决这种问题呢?很简单,在RegisterAccount 类方法定义中加上用于标识同步方法的关键字synchronized 。这样,在同步方法执行过程中该方法涉及的共享资源(在上述代码中为fBalance 成员变量)将被加上共享锁,以确保在方法运行期间只有该方法能够对共享资源进行访问,直到该方法的线程运行结束打开共享锁,其它线程才能够访问这些共享资源。在共享锁没有打开的时候其它访问共享资源的线程处于阻塞状态。
进行线程同步策略控制后的RegisterAccount 类定义如下面代码所示:
public class RegisterAccount
{
float fBalance;
public synchronized void deposit(float fFees){ fBalance += fFees; }
public synchronized void withdraw(float fFees){ fBalance -= fFees; }
… …
}
从经过线程同步机制定义后的代码形式可以看出:在对共享资源进行访问的方法访问属性关键字(public )后附加同步定义关键字synchronized ,使得同步方法在对共享资源访问的时候,为这些敏感资源附加共享锁来控制方法执行期间的资源独占性,实现了应用系统 数据资源的一致性管理和维护。
五、 Java 线程的管理
线程的状态控制
在这里需要明确的是:无论采用继承Thread 类还是实现Runnable 接口来实现应用程序 的多线程能力,都需要在该类中定义用于完成实际功能的run 方法,这个run 方法称为线程体(Thread Body )。按照线程体在计算机系统 内存中的状态不同,可以将线程分为创建、就绪、运行、睡眠、挂起和死亡等类型。这些线程状态类型下线程的特征为:
创建状态:当利用new 关键字创建线程对象实例后,它仅仅作为一个对象实例存在,JVM 没有为其分配CPU 时间片等线程运行资源;
就绪状态:在处于创建状态的线程中调用start 方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU 时间之外的其它系统 资源,只等JVM 的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU 时间片的机会。
睡眠状态:在线程运行过程中可以调用sleep 方法并在方法参数中指定线程的睡眠时间将线程状态转换为睡眠状态。这时,该线程在不释放占用资源的情况下停止运行指定的睡眠时间。时间到达后,线程重新由JVM 线程调度器进行调度和管理。
挂起状态:可以通过调用suspend 方法将线程的状态转换为挂起状态。这时,线程将释放占用的所有资源,由JVM 调度转入临时存储空间,直至应用程序 调用resume 方法恢复线程运行。
死亡状态:当线程体运行结束或者调用线程对象的stop 方法后线程将终止运行,由JVM 收回线程占用的资源。
在Java 线程类中分别定义了相应的方法,用于在应用程序 中对线程状态进行控制和管理。
线程的调度
线程调用的意义在于JVM 应对运行的多个线程进行系统 级的协调,以避免多个线程争用有限资源而导致应用系统 死机或者崩溃。
为了线程对于操作系统 和用户的重要性区分开,Java 定义了线程的优先级策略。Java 将线程的优先级分为10 个等级,分别用1-10 之间的数字表示。数字越大表明线程的级别越高。相应地,在Thread 类中定义了表示线程最低、最高和普通优先级的成员变量MIN_PRIORITY 、MAX_PRIORITY 和NORMAL_PRIORITY ,代表的优先级等级分别为1 、10 和5 。当一个线程对象被创建时,其默认的线程优先级是5 。
为了控制线程的运行策略,Java 定义了线程调度器来监控系统 中处于就绪状态的所有线程。线程调度器按照线程的优先级决定那个线程投入处理器运行。在多个线程处于就绪状态的条件下,具有高优先级的线程会在低优先级线程之前得到执行。线程调度器同样采用" 抢占式" 策略来调度线程执行,即当前线程执行过程中有较高优先级的线程进入就绪状态,则高优先级的线程立即被调度执行。具有相同优先级的所有线程采用轮转的方式来共同分配CPU 时间片。
在应用程序 中设置线程优先级的方法很简单,在创建线程对象之后可以调用线程对象的setPriority 方法改变该线程的运行优先级,同样可以调用getPriority 方法获取当前线程的优先级。
在Java 中比较特殊的线程是被称为守护(Daemon )线程的低级别线程。这个线程具有最低的优先级,用于为系统 中的其它对象和线程提供服务。将一个用户线程设置为守护线程的方式是在线程对象创建之前调用线程对象的setDaemon 方法。典型的守护线程例子是JVM 中的系统 资源自动回收线程,它始终在低级别的状态中运行,用于实时监控和管理系统 中的可回收资源。
线程分组管理
Java 定义了在多线程运行系统 中的线程组(ThreadGroup )对象,用于实现按照特定功能对线程进行集中式分组管理。用户创建的每个线程均属于某线程组,这个线程组可以在线程创建时指定,也可以不指定线程组以使该线程处于默认的线程组之中。但是,一旦线程加入某线程组,该线程就一直存在于该线程组中直至线程死亡,不能在中途改变线程所属的线程组。
当Java 的Application 应用程序 运行时,JVM 创建名称为main 的线程组。除非单独指定,在该应用程序 中创建的线程均属于main 线程组。在main 线程组中可以创建其它名称的线程组并将其它线程加入到该线程组中,依此类推,构成线程和线程组之间的树型管理和继承关系。
与线程类似,可以针对线程组对象进行线程组的调度、状态管理以及优先级设置等。在对线程组进行管理过程中,加入到某线程组中的所有线程均被看作统一的对象。
六、小结:
本文针对Java 平台中线程的性质和应用程序 的多线程策略进行了分析和讲解。
与其它操作系统 环境不同,Java 运行环境中的线程类似于多用户、多任务操作系统 环境下的进程,但在进程和线程的运行及创建方式等方面,进程与Java 线程具有明显区别。
Unix 操作系统 环境下,应用程序 可以利用fork 函数创建子进程,但子进程与该应用程序 进程拥有独立的地址空间、系统 资源和代码执行单元,并且进程的调度是由操作系统 来完成的,使得在应用进程之间进行通信和线程协调相对复杂。而Java 应用程序 中的多线程则是共享同一应用系统 资源的多个并行代码执行体,线程之间的通信和协调方法相对简单。
可以说:Java 语言对应用程序 多线程能力的支持增强了Java 作为网络 程序 设计语言的优势,为实现分布式应用系统 中多客户端的并发访问以及提高服务器 的响应效率奠定坚实基础。
来源于 : http://www.myfaq.com.cn/A/2005-05-03/142139.html
随着计算机技术的发展,编程模型也越来越复杂多样化。但多线程编程模型是目前计算机系统架构的最终模型。随着CPU 主频的不断攀升,X86 架构的硬件已经成为瓶,在这种架构的CPU 主频最高为4G 。事实上目前3.6G 主频的CPU 已经接近了顶峰。
如果不能从根本上更新当前CPU 的架构( 在很长一段时间内还不太可能) ,那么继续提高CPU 性能的方法就是超线程CPU 模式。那么,作业系统、应用程序要发挥CPU 的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式应用程序。
所以,掌握多线程编程模型,不仅是目前提高应用性能的手段,更是下一代编程模型的核心思想。多线程编程的目的,就是" 最大限度地利用CPU 资源" ,当某一线程的处理不需要占用CPU 而只和I/O,OEMBIOS 等资源打交道时,让需要占用CPU 资源的其它线程有机会获得CPU 资源。从根本上说,这就是多线程编程的最终目的。
[ 第一需要弄清的问题]
如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别。
线程对象是可以产生线程的对象。比如在java 平台中Thread 对象,Runnable 对象。线程,是指正在执行的一个指点令序列。在java 平台上是指从一个线程对象的start() 开始,运行run 方法体中的那一段相对独立的过程。
鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。
天下难事必始于易,天下大事必始于细。
让我们先从最简单的" 单线程" 来入手:(1) 带引号说明只是相对而言的单线程,(2) 基于java 。
class BeginClass{
public static void main(String[] args){
for(int i=0;i<100;i++)
System.out.println("Hello,World!");
}
}
如果我们成功编译了该java 文件,然后在命令行上敲入:
java BeginClass
现在发生了什么呢?每一个java 程序员,从他开始学习java 的第一分钟里都会接触到这个问题,但是,你知道它到底发生发什么?
JVM 进程被启动,在同一个JVM 进程中,有且只有一个进程,就是它自己。然后在这个JVM 环境中,所有程序的运行都是以线程来运行。JVM 最先会产生一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main 方法开始运行。当main 方法结束后,主线程运行完成。JVM 进程也随之退出。
我们看到的是一个主线程在运行main 方法,这样的只有一个线程执行程序逻辑的流程我们称
之为单线程。这是JVM 提供给我们的单线程环境,事实上,JVM 底层还至少有垃圾回收这样的后台线程以及其它非java 线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。
主线程是JVM 自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main 方法这个指令序列。理解它,但它没有更多可以研究的内容。
[ 接触多线程]
class MyThread extends Thread{
public void run(){
System.out.println("Thread say:Hello,World!");
}
}
public class MoreThreads{
public static void main(String[] args){
new MyThread();
new MyThread().start();
System.out.println("Main say:Hello,World");
}
}
执行这个程序,main 方法第一行产生了一个线程对象,但并没有线程启动。
main 方法第二行产生了一个线程对象,并启动了一个线程。
main 方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。
我们先不研究Thread 对象的具体内容,稍微来回想一下上面的两个概念,线程对象和线程。在JAVA 中,线程对象是JVM 产生的一个普通的Object 子类。而线程是CPU 分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。
累了吧?为不么不继续了?
基于这种风格来介绍多线程,并不是每个人都喜欢和接受的,如果你不喜欢,正好不浪费你的时间了,而如果你接受的话,那就看下一节吧。
进程(Process )是最初定义在Unix 等多用户、多任务操作系统 环境下用于表示应用程序 在内存环境中基本执行单元的概念。以Unix 操作系统 为例,进程是Unix 操作系统 环境中的基本成分、是系统 资源分配的基本单位。Unix 操作系统 中完成的几乎所有用户管理和资源分配等工作都是通过操作系统 对应用程序 进程的控制来实现的。
C 、C++ 、Java 等语言编写的源程序 经相应的编译器编译成可执行文件 后,提交给计算机处理器运行。这时,处在可执行状态中的应用程序 称为进程。从用户角度来看,进程是应用程序 的一个执行过程。从操作系统 核心角度来看,进程代表的是操作系统 分配的内存、CPU 时间片等资源的基本单位,是为正在运行的程序 提供的运行环境。进程与应用程序 的区别在于应用程序 作为一个静态文件 存储在计算机系统 的硬盘等存储空间中,而进程则是处于动态条件下由操作系统 维护的系统 资源管理实体。多任务环境下应用程序 进程的主要特点包括:
● 进程在执行过程中有内存单元的初始入口点,并且进程存活过程中始终拥有独立的内存地址空间;
● 进程的生存期状态包括创建、就绪、运行、阻塞和死亡等类型;
● 从应用程序 进程在执行过程中向CPU 发出的运行指令形式不同,可以将进程的状态分为用户态和核心态。处于用户态下的进程执行的是应用程序 指令、处于核心态下的应用程序 进程执行的是操作系统 指令。
在Unix 操作系统 启动过程中,系统 自动创建swapper 、init 等系统 进程,用于管理内存资源以及对用户进程进行调度等。在Unix 环境下无论是由操作系统 创建的进程还要由应用程序 执行创建的进程,均拥有唯一的进程标识(PID )。
二、进程与Java 线程的区别
应用程序 在执行过程中存在一个内存空间的初始入口点地址、一个程序 执行过程中的代码执行序列以及用于标识进程结束的内存出口点地址,在进程执行过程中的每一时间点均有唯一的处理器指令与内存单元地址相对应。
Java 语言中定义的线程(Thread )同样包括一个内存入口点地址、一个出口点地址以及能够顺序执行的代码序列。但是进程与线程的重要区别在于线程不能够单独执行,它必须运行在处于活动状态的应用程序 进程中,因此可以定义线程是程序 内部的具有并发性的顺序代码流。
Unix 操作系统 和Microsoft Windows 操作系统 支持多用户、多进程的并发执行,而Java 语言支持应用程序 进程内部的多个执行线程的并发执行。多线程的意义在于一个应用程序 的多个逻辑单元可以并发地执行。但是多线程并不意味着多个用户进程在执行,操作系统 也不把每个线程作为独立的进程来分配独立的系统 资源。进程可以创建其子进程,子进程与父进程拥有不同的可执行代码和数据内存空间。而在用于代表应用程序 的进程中多个线程共享数据内存空间,但保持每个线程拥有独立的执行堆栈和程序 执行上下文(Context )。
基于上述区别,线程也可以称为轻型进程 (Light Weight Process ,LWP) 。不同线程间允许任务协作和数据交换,使得在计算机系统 资源消耗等方面非常廉价。
线程需要操作系统 的支持,不是所有类型的计算机都支持多线程应用程序 。Java 程序 设计语言将线程支持与语言运行环境结合在一起,提供了多任务并发执行的能力。这就好比一个人在处理家务的过程中,将衣服放到洗衣机中自动洗涤后将大米放在电饭锅里,然后开始做菜。等菜做好了,饭熟了同时衣服也洗好了。
需要注意的是:在应用程序 中使用多线程不会增加 CPU 的数据处理能力。只有在多CPU 的计算机或者在网络 计算体系结构下,将Java程序 划分为多个并发执行线程后,同时启动多个线程运行,使不同的线程运行在基于不同处理器的Java 虚拟机中,才能提高应用程序 的执行效率。
另外,如果应用程序 必须等待网络 连接或数据库 连接等数据吞吐速度相对较慢的资源时,多线程应用程序 是非常有利的。基于Internet 的应用程序 有必要是多线程类型的,例如,当开发要支持大量客户机的服务器 端应用程序 时,可以将应用程序 创建成多线程形式来响应客户端的连接请求,使每个连接用户独占一个客户端连接线程。这样,用户感觉服务器 只为连接用户自己服务,从而缩短了服务器 的客户端响应时间。
三、Java 语言的多线程程序 设计方法
利用Java 语言实现多线程应用程序 的方法很简单。根据多线程应用程序 继承或实现对象的不同可以采用两种方式:一种是应用程序 的并发运行对象直接继承Java 的线程类Thread ;另外一种方式是定义并发执行对象实现Runnable 接口。
继承Thread 类的多线程程序 设计方法
Thread 类是JDK 中定义的用于控制线程对象的类,在该类中封装了用于进行线程控制的方法。见下面的示例代码:
//Consumer.java import java .util.*; class Consumer extends Thread { int nTime; String strConsumer; public Consumer(int nTime, String strConsumer) { this.nTime = nTime; this.strConsumer = strConsumer; } public void run() { while(true) { try { System.out.println("Consumer name:"+strConsumer+"\n"); Thread.sleep(nTime); } catch(Exception e) { e.printStackTrace(); } } } static public void main(String args[]) { Consumer aConsumer = new Consumer (1000, "aConsumer"); aConsumer.start(); Consumer bConsumer = new Consumer (2000, "bConsumer"); bConsumer.start(); Consumer cConsumer = new Consumer (3000, "cConsumer "); cConsumer.start(); } }
从上面的程序 代码可以看出:多线程执行地下Consumer 继承Java 语言中的线程类Thread 并且在main 方法中创建了三个Consumer 对象的实例。当调用对象实例的start 方法时,自动调用Consumer 类中定义的run 方法启动对象线程运行。线程运行的结果是每间隔nTime 时间打印出对象实例中的字符 串成员变量strConsumer 的内容。
可以总结出继承Thread 类的多线程程序 设计方法是使应用程序 类继承Thread 类并且在该类的run 方法中实现并发性处理过程。
实现Runnable 接口的多线程程序 设计方法
Java 语言中提供的另外一种实现多线程应用程序 的方法是多线程对象实现Runnable 接口并且在该类中定义用于启动线程的run 方法。这种定义方式的好处在于多线程应用对象可以继承其它对象而不是必须继承Thread 类,从而能够增加类定义的逻辑性。
实现Runnable 接口的多线程应用程序 框架代码如下所示:
//Consumer.java
import java .util.*;
class Consumer implements Runnable
{
… …
public Consumer(int nTime, String strConsumer){… …}
public void run(){… …}
static public void main(String args[])
{
Thread aConsumer = new Thread(new Consumer(1000, "aConsumer"));
aConsumer.start();
// 其它对象实例的运行线程
//… …
}
}
从上述代码可以看出:该类实现了Runnable 接口并且在该类中定义了run 方法。这种多线程应用程序 的实现方式与继承Thread 类的多线程应用程序 的重要区别在于启动多线程对象的方法设计方法不同。在上述代码中,通过创建Thread 对象实例并且将应用对象作为创建Thread 类实例的参数。
四、线程间的同步
Java 应用程序 的多个线程共享同一进程的数据资源,多个用户线程在并发运行过程中可能同时访问具有敏感性的内容。在Java 中定义了线程同步的概念,实现对共享资源的一致性维护。下面以笔者最近开发的移动通信计费系统 中线程间同步控制方法,说明Java 语言中多线程同步方式的实现过程。
在没有多线程同步控制策略条件下的客户账户类定义框架代码如下所示:
public class RegisterAccount
{
float fBalance;
// 客户缴费方法
public void deposit(float fFees){ fBalance += fFees; }
// 通话计费方法
public void withdraw(float fFees){ fBalance -= fFees; }
… …
}
读者也许会认为:上述程序 代码完全能够满足计费系统 实际的需要。确实,在单线程环境下该程序 确实是可靠的。但是,多进程并发运行的情况是怎样的呢?假设发生这种情况:客户在客户服务中心进行缴费的同时正在利用移动通信设备仅此通话,客户通话结束时计费系统 启动计费进程,而同时服务中心的工作人员也提交缴费进程运行。读者可以看到如果发生这种情况,对客户账户的处理是不严肃的。
如何解决这种问题呢?很简单,在RegisterAccount 类方法定义中加上用于标识同步方法的关键字synchronized 。这样,在同步方法执行过程中该方法涉及的共享资源(在上述代码中为fBalance 成员变量)将被加上共享锁,以确保在方法运行期间只有该方法能够对共享资源进行访问,直到该方法的线程运行结束打开共享锁,其它线程才能够访问这些共享资源。在共享锁没有打开的时候其它访问共享资源的线程处于阻塞状态。
进行线程同步策略控制后的RegisterAccount 类定义如下面代码所示:
public class RegisterAccount
{
float fBalance;
public synchronized void deposit(float fFees){ fBalance += fFees; }
public synchronized void withdraw(float fFees){ fBalance -= fFees; }
… …
}
从经过线程同步机制定义后的代码形式可以看出:在对共享资源进行访问的方法访问属性关键字(public )后附加同步定义关键字synchronized ,使得同步方法在对共享资源访问的时候,为这些敏感资源附加共享锁来控制方法执行期间的资源独占性,实现了应用系统 数据资源的一致性管理和维护。
五、 Java 线程的管理
线程的状态控制
在这里需要明确的是:无论采用继承Thread 类还是实现Runnable 接口来实现应用程序 的多线程能力,都需要在该类中定义用于完成实际功能的run 方法,这个run 方法称为线程体(Thread Body )。按照线程体在计算机系统 内存中的状态不同,可以将线程分为创建、就绪、运行、睡眠、挂起和死亡等类型。这些线程状态类型下线程的特征为:
创建状态:当利用new 关键字创建线程对象实例后,它仅仅作为一个对象实例存在,JVM 没有为其分配CPU 时间片等线程运行资源;
就绪状态:在处于创建状态的线程中调用start 方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU 时间之外的其它系统 资源,只等JVM 的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU 时间片的机会。
睡眠状态:在线程运行过程中可以调用sleep 方法并在方法参数中指定线程的睡眠时间将线程状态转换为睡眠状态。这时,该线程在不释放占用资源的情况下停止运行指定的睡眠时间。时间到达后,线程重新由JVM 线程调度器进行调度和管理。
挂起状态:可以通过调用suspend 方法将线程的状态转换为挂起状态。这时,线程将释放占用的所有资源,由JVM 调度转入临时存储空间,直至应用程序 调用resume 方法恢复线程运行。
死亡状态:当线程体运行结束或者调用线程对象的stop 方法后线程将终止运行,由JVM 收回线程占用的资源。
在Java 线程类中分别定义了相应的方法,用于在应用程序 中对线程状态进行控制和管理。
线程的调度
线程调用的意义在于JVM 应对运行的多个线程进行系统 级的协调,以避免多个线程争用有限资源而导致应用系统 死机或者崩溃。
为了线程对于操作系统 和用户的重要性区分开,Java 定义了线程的优先级策略。Java 将线程的优先级分为10 个等级,分别用1-10 之间的数字表示。数字越大表明线程的级别越高。相应地,在Thread 类中定义了表示线程最低、最高和普通优先级的成员变量MIN_PRIORITY 、MAX_PRIORITY 和NORMAL_PRIORITY ,代表的优先级等级分别为1 、10 和5 。当一个线程对象被创建时,其默认的线程优先级是5 。
为了控制线程的运行策略,Java 定义了线程调度器来监控系统 中处于就绪状态的所有线程。线程调度器按照线程的优先级决定那个线程投入处理器运行。在多个线程处于就绪状态的条件下,具有高优先级的线程会在低优先级线程之前得到执行。线程调度器同样采用" 抢占式" 策略来调度线程执行,即当前线程执行过程中有较高优先级的线程进入就绪状态,则高优先级的线程立即被调度执行。具有相同优先级的所有线程采用轮转的方式来共同分配CPU 时间片。
在应用程序 中设置线程优先级的方法很简单,在创建线程对象之后可以调用线程对象的setPriority 方法改变该线程的运行优先级,同样可以调用getPriority 方法获取当前线程的优先级。
在Java 中比较特殊的线程是被称为守护(Daemon )线程的低级别线程。这个线程具有最低的优先级,用于为系统 中的其它对象和线程提供服务。将一个用户线程设置为守护线程的方式是在线程对象创建之前调用线程对象的setDaemon 方法。典型的守护线程例子是JVM 中的系统 资源自动回收线程,它始终在低级别的状态中运行,用于实时监控和管理系统 中的可回收资源。
线程分组管理
Java 定义了在多线程运行系统 中的线程组(ThreadGroup )对象,用于实现按照特定功能对线程进行集中式分组管理。用户创建的每个线程均属于某线程组,这个线程组可以在线程创建时指定,也可以不指定线程组以使该线程处于默认的线程组之中。但是,一旦线程加入某线程组,该线程就一直存在于该线程组中直至线程死亡,不能在中途改变线程所属的线程组。
当Java 的Application 应用程序 运行时,JVM 创建名称为main 的线程组。除非单独指定,在该应用程序 中创建的线程均属于main 线程组。在main 线程组中可以创建其它名称的线程组并将其它线程加入到该线程组中,依此类推,构成线程和线程组之间的树型管理和继承关系。
与线程类似,可以针对线程组对象进行线程组的调度、状态管理以及优先级设置等。在对线程组进行管理过程中,加入到某线程组中的所有线程均被看作统一的对象。
六、小结:
本文针对Java 平台中线程的性质和应用程序 的多线程策略进行了分析和讲解。
与其它操作系统 环境不同,Java 运行环境中的线程类似于多用户、多任务操作系统 环境下的进程,但在进程和线程的运行及创建方式等方面,进程与Java 线程具有明显区别。
Unix 操作系统 环境下,应用程序 可以利用fork 函数创建子进程,但子进程与该应用程序 进程拥有独立的地址空间、系统 资源和代码执行单元,并且进程的调度是由操作系统 来完成的,使得在应用进程之间进行通信和线程协调相对复杂。而Java 应用程序 中的多线程则是共享同一应用系统 资源的多个并行代码执行体,线程之间的通信和协调方法相对简单。
可以说:Java 语言对应用程序 多线程能力的支持增强了Java 作为网络 程序 设计语言的优势,为实现分布式应用系统 中多客户端的并发访问以及提高服务器 的响应效率奠定坚实基础。
来源于 : http://www.myfaq.com.cn/A/2005-05-03/142139.html
随着计算机技术的发展,编程模型也越来越复杂多样化。但多线程编程模型是目前计算机系统架构的最终模型。随着CPU 主频的不断攀升,X86 架构的硬件已经成为瓶,在这种架构的CPU 主频最高为4G 。事实上目前3.6G 主频的CPU 已经接近了顶峰。
如果不能从根本上更新当前CPU 的架构( 在很长一段时间内还不太可能) ,那么继续提高CPU 性能的方法就是超线程CPU 模式。那么,作业系统、应用程序要发挥CPU 的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式应用程序。
所以,掌握多线程编程模型,不仅是目前提高应用性能的手段,更是下一代编程模型的核心思想。多线程编程的目的,就是" 最大限度地利用CPU 资源" ,当某一线程的处理不需要占用CPU 而只和I/O,OEMBIOS 等资源打交道时,让需要占用CPU 资源的其它线程有机会获得CPU 资源。从根本上说,这就是多线程编程的最终目的。
[ 第一需要弄清的问题]
如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别。
线程对象是可以产生线程的对象。比如在java 平台中Thread 对象,Runnable 对象。线程,是指正在执行的一个指点令序列。在java 平台上是指从一个线程对象的start() 开始,运行run 方法体中的那一段相对独立的过程。
鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。
天下难事必始于易,天下大事必始于细。
让我们先从最简单的" 单线程" 来入手:(1) 带引号说明只是相对而言的单线程,(2) 基于java 。
class BeginClass{
public static void main(String[] args){
for(int i=0;i<100;i++)
System.out.println("Hello,World!");
}
}
如果我们成功编译了该java 文件,然后在命令行上敲入:
java BeginClass
现在发生了什么呢?每一个java 程序员,从他开始学习java 的第一分钟里都会接触到这个问题,但是,你知道它到底发生发什么?
JVM 进程被启动,在同一个JVM 进程中,有且只有一个进程,就是它自己。然后在这个JVM 环境中,所有程序的运行都是以线程来运行。JVM 最先会产生一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main 方法开始运行。当main 方法结束后,主线程运行完成。JVM 进程也随之退出。
我们看到的是一个主线程在运行main 方法,这样的只有一个线程执行程序逻辑的流程我们称
之为单线程。这是JVM 提供给我们的单线程环境,事实上,JVM 底层还至少有垃圾回收这样的后台线程以及其它非java 线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。
主线程是JVM 自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main 方法这个指令序列。理解它,但它没有更多可以研究的内容。
[ 接触多线程]
class MyThread extends Thread{
public void run(){
System.out.println("Thread say:Hello,World!");
}
}
public class MoreThreads{
public static void main(String[] args){
new MyThread();
new MyThread().start();
System.out.println("Main say:Hello,World");
}
}
执行这个程序,main 方法第一行产生了一个线程对象,但并没有线程启动。
main 方法第二行产生了一个线程对象,并启动了一个线程。
main 方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。
我们先不研究Thread 对象的具体内容,稍微来回想一下上面的两个概念,线程对象和线程。在JAVA 中,线程对象是JVM 产生的一个普通的Object 子类。而线程是CPU 分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。
累了吧?为不么不继续了?
基于这种风格来介绍多线程,并不是每个人都喜欢和接受的,如果你不喜欢,正好不浪费你的时间了,而如果你接受的话,那就看下一节吧。
发表评论
-
System.gc()与Runtime.getRuntime().gc()区别
2019-07-23 14:34 840首先,我们需要明确一点的是,两个gc都会强制触发垃圾收集,它们 ... -
sql大全
2013-07-12 19:02 1038经典SQL语句大全 一、基础 1、说明: ... -
ATAL ERROR in native method: JDWP No transports initialized, jvmtiError=JVMTI_ER
2013-07-04 15:38 4041windos系统bug 因为我的机器比较内存比较少512M, ... -
java读取文本文件数据
2013-01-24 23:41 1095import java.io.*; public cla ... -
java复习(set 、list、map)
2013-01-24 23:27 1148复习 public static void main( ... -
java代码实现excel输到导入oracle
2012-07-31 13:28 15051.首先需要两个jar包jxl.jar,ojdbc.jar(注 ... -
使用doc命令将java工程生成.jar文件和war文件
2011-12-30 09:56 1317假定有一个Web应用:C:\myHome myHom ... -
堆栈 新的 认识
2011-10-31 14:24 1241A a = new A(); 在堆开辟空间,并把值存在堆 ... -
java实现捕捉屏幕
2011-10-15 16:17 1694要使用的是java.util.Robot类来捕获屏幕,可以实现 ... -
抽象类、抽象方法、接口
2011-10-13 10:55 1140抽象类就是不能使用new方法进行实例化的类,即没有具体实例对象 ... -
java死锁
2011-10-10 22:05 878package cn.com.io.threadDem ... -
for和foreach使用?
2011-08-29 15:03 708在JDK5.0中加入了一个新的特性,那就是FOR-EACH循环 ... -
java开发群
2011-08-25 20:08 41欢迎java群1670293,希望有工作经验热情的加入---- ... -
JAVA中,如何判断一个字符串中包含的字符在另一个字符前面?
2011-06-30 13:34 3614[color=indigo]用它们在这个字符串中的位置来判断。 ... -
java实现判断A中是否包含B
2011-06-30 13:33 1330代码 package day6; public cla ... -
导入word到Fckeditor(java实现)
2011-06-24 13:58 1187最近项目可以说到达了一个里程碑,借这篇文章把前面的技术进行总结 ... -
使用3中不同的方式 从集合中取数据
2011-05-25 10:47 977代码 package iter; import jav ... -
Java反射
2011-05-05 08:49 855[color=blue]Java Reflection (JA ... -
Java反射机制
2011-05-05 08:48 746JAVA反射机制 JAVA ... -
get 和post
2011-05-05 08:39 847表单form的提交有两种方式,一种是get的方法,一种是pos ...
相关推荐
"大漠多线程模板"是一个专门针对C#开发的多线程处理框架,它为开发者提供了便捷的方式来管理和优化多线程应用。这个框架由知名开发者"大漠"创建,旨在简化复杂的并发编程,提高代码的可读性和可维护性。 多线程允许...
在IT行业中,多线程是一种常见的编程技术,它允许程序同时执行多个独立的任务,从而提高计算机系统的效率和响应性。特别是在自动化工具如“按键精灵”中,多线程的应用能够显著提升其性能和实用性。 标题“多线程_...
标题中的“pb9多线程控件”指的是在PowerBuilder 9.0(PB9)环境中,使用的一种能够实现真正多线程功能的组件或技术。PowerBuilder是一款经典的面向对象的开发工具,主要用于构建数据库应用系统。在PB的早期版本中,...
在编程领域,多线程是实现并发执行任务的重要机制,特别是在现代计算机系统中,多核处理器使得多线程成为提高程序性能的关键手段。C#语言提供了丰富的多线程支持,让我们能够编写出高效的多线程应用程序。在这个"多...
在IT领域,多线程编程是一项关键技能,尤其是在性能优化和并发处理方面。本文将深入探讨多线程编程的基础知识,以帮助初学者快速入门。 首先,我们需要理解什么是多线程。多线程是指在一个进程中同时执行多个独立的...
基于SpringBoot和POI实现单线程和多线程导出Excel.zip基于SpringBoot和POI实现单线程和多线程导出Excel.zip基于SpringBoot和POI实现单线程和多线程导出Excel.zip基于SpringBoot和POI实现单线程和多线程导出Excel.zip...
在.NET框架中,C#语言提供了强大的多线程支持,使得开发者可以充分利用现代多核处理器的优势,实现并行处理和高效能编程。本资源包含六个C#.NET多线程的实例,涵盖了多线程的基本使用到更高级的概念,如线程互斥。...
在编程领域,多线程是实现并发执行任务的重要机制,特别是在易语言中,它能有效提升程序的执行效率。易语言是一种中文编程语言,旨在降低编程门槛,让普通用户也能进行程序开发。本文将深入探讨易语言中的多线程以及...
本文将详细探讨PB(包括PB9、PB12.5以及PB.NET)实现多线程的方法。 一、PB9的多线程实现 在PB9中,虽然官方并未直接支持多线程,但开发者可以通过使用Windows API函数来实现。一种常见的方式是创建一个新的窗口类...
在IT行业中,多线程是程序设计中的一个重要概念,尤其在Java编程中,它被广泛应用于提高应用程序的并发性能和响应速度。本压缩包“多线程基础与基于多线程的简单聊天室”提供了对多线程技术的实践理解和二次开发的...
在编程领域,尤其是在开发高效、响应迅速的应用程序时,多线程技术扮演着至关重要的角色。Qt5框架提供了一种方便的方式来实现多线程,它允许开发者在不同的线程中执行任务,从而避免主线程(GUI线程)因处理耗时操作...
"鱼刺多线程模块"是一个专为提升程序运行效率而设计的开源组件,它主要聚焦于多线程技术的应用。在计算机科学中,多线程是并发执行多个任务或子任务的一种方法,使得程序能够更高效地利用系统资源,特别是在多核...
Qt 多线程及简单实例 demo。 多线程的几大特点: 1.多线程的执行顺序无法保证,与操作系统的调度策略和线程优先级等因素有关。 2.多线程的切换可能发生在任何时刻、任何地点。 3.多线程对代码的敏感度高,因此对...
Linux 下 C 语言多线程编程实例 Linux 下的多线程编程是一种非常重要的技术,在实际应用中有非常广泛的应用范围。多线程编程可以大大提高程序的执行效率和响应速度。但是,多线程编程也存在一些复杂性,例如线程...
单线程和多线程是计算机程序执行时的两种不同模型,它们在处理并发任务、资源管理和性能上有着显著的差异。理解这两种模型是编程尤其是服务器端开发的基础,尤其是在Java、C#等支持多线程的编程语言中。 首先,让...
在Delphi编程中,多线程技术被广泛用于提高应用程序的执行效率,特别是在处理大量数据或执行长时间操作时。DLL(动态链接库)是Windows操作系统中的一个重要组件,它允许代码和资源在多个程序之间共享。当需要在多...
在C#编程中,多线程技术常用于提高应用程序的执行效率,特别是在处理数据库操作时。SQLite是一款轻量级、嵌入式的关系型数据库,它广泛应用于桌面应用、移动设备和Web开发。当多线程环境对SQLite进行读写操作时,...
Qt中利用OpenCV2.4.4多线程打开多摄像机 每个线程处理一个摄像机,从中拿出帧显示到主线程的Label控件上 模拟了一个16个摄像机的场景,有不开多线程和打开多线程的对比。 可以明显感觉到打开多线程后主界面不卡了。 ...
### 可并行递归算法的递归多线程实现:深入解析 #### 引言:多线程与并行处理的重要性 随着计算任务日益复杂,传统的单线程编程模型已无法满足高效处理大规模数据的需求。多线程编程作为一种提高程序并发性和性能...
"鱼刺多线程注册源码例子"是一个基于"鱼刺多线程稳定框架"的编程实践,旨在展示如何在软件开发中有效地利用多线程技术来提高程序的执行效率和稳定性。在这个例子中,"鱼刺框架"可能是一个专门为多线程编程设计的开源...