Chinaunix首页 | 论坛 | 博客
  • 博客访问: 539436
  • 博文数量: 260
  • 博客积分: 10435
  • 博客等级: 上将
  • 技术积分: 1939
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-24 14:50
文章分类

全部博文(260)

文章存档

2011年(22)

2010年(209)

2009年(29)

我的朋友

分类: Java

2010-11-01 17:41:43

1.Java 中的线程

    线程允许程序同一时间做很多任务,至少,看起来那些任务是并发执行的。在我的并发编程的帖子里有介绍线程的基本概念;我们知道在任一特定时刻仅有一个线程 在执行,但是 CPU 给每个线程一小片时间运行(通过时间片)然后来回在线程间快速的切换。这就是我们所看到的多线程运行的样子。这里的cpu指的是单处理器的情况,因为对于 多处理器的话,多线程调度机制将会将各个任务分配到不同处理器上来实现,这样cpu时间片切片的行为就并不频繁,也就大大提升了程序的执行效率了;

    Java 语言使用 Thread 类内建了对线程的支持。当一个线程被通知运行,该线程上的任务对应的 run() 方法就被执行。线程的启动是调用Thread的start方法,当然线程启动不一定就一定会被执行,这和它说对应的资源环境存在关系;如果资源忙的话,则 线程将处于阻塞的状态;

·线程的生命周期

    线程在它的生命周期中会是几种可能的状态中的一种。在同一时刻只能处于一种状态,这些状态是由 JVM 指示的,而不是操作系统。线程状态列示如下:

    ·新建

    

Java代码 企业级任务调度框架Quartz 七 线程在Quartz里的意义(1) - 雪中飞 - 品位人生

  1. Thread thread=new Thread(new SimpleTask());  

Thread thread=new Thread(new SimpleTask());

     当一个新的线程被创建后,它就被指定为新建状态。线程在此状态时不会被分配任何系统资源

    ·可运行

    

Java代码 企业级任务调度框架Quartz 七 线程在Quartz里的意义(1) - 雪中飞 - 品位人生

  1. thread.start();  

thread.start();

     要使线程转到可运行状态,必须调用 start() 方法。

    ·阻塞

      一个线程如果已经启动,则在其任务处理的过程中产生系统资源不足,或系统资源被锁,则该线程将被阻塞;则等待系统资源释放后,线程会继续执行任务;

    ·等待中

   

Java代码 企业级任务调度框架Quartz 七 线程在Quartz里的意义(1) - 雪中飞 - 品位人生

  1. thread.sleep();  

thread.sleep();

    ·指定时间的等待

   

Java代码 企业级任务调度框架Quartz 七 线程在Quartz里的意义(1) - 雪中飞 - 品位人生

  1. thread.wait();  

thread.wait();

    ·结束

   

Java代码 企业级任务调度框架Quartz 七 线程在Quartz里的意义(1) - 雪中飞 - 品位人生

  1. Thread.yield();  

Thread.yield();

    当调用了新建线程的 start() 方法,线程进入到就绪状态并被认为是运行中的。这不代表这个线程实际正在执行指令。即使线程的状态被设置为可运行,已安排去运行,它或许不得不等到其他执 行中的线程结束或被临时挂起。JVM 来决定哪个线程该获得下次运行的机会。JVM 决定下个线程的行为依赖于数个因素,包括操作系统、特定的 JVM 实现和线程优先级及其他因素。 

 

2.线程与进程之间的区别
   每一个 Java 程序被指派一个进程。程序接着把任务分派到线程中。甚至是你在写一个仅包含一个 main() 方法的 "Hello, World" 程序,程序仍然要使用线程,虽然只有一个。
有时候,所谓“轻量级进程”指的就是线程。这与实际的操作系统进程“重量级”一词形成对比。假如你正在使用 Windows 系统,你可以在任务管理器中看到运行着的重量级进程,但是你看不到线程;

 

从上面看出,对于一个单cpu的机器,线程通常不会并发着运行的,同一时刻只能有一个线程才 能得到 CPU 的参与执行。每一个 JVM 和操作系统对此处理方式可能有所不同,然而,有时,一个线程可能要执行完成后,另一线程才能接着运行。这就是所知的“非抢先式”。其他的,一个线程中途被 打断来让其他线程做它们的事。这被称做“抢先式”。一般的,线程必须完成运行后另一线程才能获得 CPU,这种对 CPU 资源的竟争通常是基于线程优先级的;

3.什么是线程优先级?
   我们已多次提到,可运行线程是否能获得下次运行机会是由 JVM 来决定的。JVM 支持的一种知名的调度方案“固定优先级调度”,它调度线程是基于它们与其他可运行线程优先级相比较而定的。
优 先级是一个指定给新线程的整数值,它还应参考创建它的父线程的优先级。这个值从 Thread.MIN_PRIORITY (等于 1),到 Thread.MAX_PRIORITY (等于 10)。这些常量值在 java.lang.Thread 类中有定义的,这个值越大 (最大到 MAX_PRIORITY),所具有的优先级就越高。除非特别指定,Java 程序中多数刚创建的线程运线在 Thread.NORMAL_PRIORITY (恰好是个中值 5) 级别上。你可能用 setPriority() 方法来修改线程的优先级。

通常,一个运行着的线程持续运行直到下面情况中的一个:

    ·线程让出 CPU (可能是调用了它的 sleep() 方法、yield() 方法或者是 Object.wait() 方法)。

    ·线程的 run() 结束了。

    ·OS 支持时间片的话,它的时间到了

    ·另一高优先级的线程变为了可运行状态

4.什么叫做守护线程?
    守护线程有时指的是服务线程,因为他们运行在很低的优先级别上(在后台),完成些非紧急但却本质的任务。例如,Java 的垃圾回收器就是一个守护线程的例子。这个线程运行在后台,查找并回收程序不再使用的内存。你可以通过传递 true 给 setDaemon() 方法使线程成为守护线程。不然,这个线程就是一个用户线程。你仅能在线程启动之前把它设置为守护线程。守护线程有点特别的就是假如只有守护线程在运行而没 有活着的非守护线程,此时 JVM 仍然是存在的。

5. Java 线程组和线程池
    线程组:Java 的 ThreadGroup 由 java.lang.ThreadGroup 类实现的,描绘的是一组行为如单一实体般的线程。每一个 Java 线程都是线程组的成员。在一个线程组中,你可以停止或启动所有的线程组成员。你还可以设置线程优先级和执行其他线程的通用功能。线程组对于构建像 Quartz 那样的多线程程序是非常有用的。当一个 Java 程序启动后,它就创建了一个叫做 main 的 ThreadGrop。除非特别指定,所有创建的线程都会成为这个组的成员。当一个线程被指定到某个 ThreadGroup 时,它才会被改变。
     线程迟:Java 1.5 引入一个新的概念到 Java 语言中,称之线程池。第一眼看来,这有些像是线程组,但是它实际是用于不同目的的。尽量多个线程能从属于一个相同的 ThreadGroup 中,组是享用不到典型的池化资源带来的好处的。这就是说,线程组仅仅是用于对成员的组织。线程池是可共享的资源,是一个能被用来执行任务的可管理的线程集 合。线程池(通常讲的资源池) 比非池化资源提供了几点好处。首先也是首要的,资源池通过减少过度的对象创建改善了性能。假如你实例化十个线程,并重复的使用它们,而不是每次需要一个都 创建一个新的线程,你将会改善程序的性能。这个概念在 Java 和 J2EE 领域中比比皆是。另外的优点包括能限制资源的数量,这有助于程序的稳定性和可扩展性。这是一个非常有意义的特征,而不管你所用的是什么语言,或是什么程 序。


企业级任务调度框架Quartz 八 线程在Quartz里的意义(2)

前序:
     做为企业里的任务调度框架,出现同一时间点同时运行两个任务,或者两个任务因为开始的执行时间和执行时间的长短,很有可能出现任务并发执行的情况;因为Quartz的实现是采用java编程,那么多线程机制就是解决并发问题的必要手段了;

     线程与 Quartz 来说尤为重要,因为 Quartz  就是设计为支持同时运行多个 Job。为达到此效果,Quartz 非常倚重于内建于 Java 语言的线程,借助于自己的类和借口还有所增强。

     当 Quartz Schduler 首次由某个工厂方法创建时,工厂配置了 Scheduler 会在它的整个生命周期中用到的几个重要的资源。其中一些重要的资源是与线程相关的。提醒下大家,常见的Schduler工厂类为 DirectoSchedulerFactory 和StdSchedulerFactory;

1.主处理线程:QuartzSchedulerThread
     Quartz 应用第一次运行时,main 线程会启动 Scheduler。QuartzScheduler 被创建并创建一个 org.quartz.core.QuartzSchedulerThread 类的实例。QuartzSchedulerThread 包含有决定何时下一个 Job 将被触发的处理循环。顾名思义,QuartzSchedulerThread 是一个 Java 线程。它作为一个非守护线程运行在正常优先级下。为了更好的解释上面的这段话,我们可以将QuartzSchedulerThread 对应的线程看成一个main线程,而没有由触发器触发的任务,看成该main方法里所创建的若干线程;

QuartzSchedulerThread 的主处理循环的职责描述如下:

1. 当 Scheduler 正在运行时:

    A. 检查是否有转换为 standby 模式的请求。

    1. 假如 standby 方法被调用,等待继续的信号

    B. 询问 JobStore 下次要被触发的 Trigger.
   JobStore:要使 Job 存储在内存中需通过设置org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore。假如我们不希望在 JVM 退出之后丢失调度器的状态信息的话,我们可以使用关系型数据库来存储这些信息。这需要另一个作业存储实现(JobStore);
   1. 如果没有 Trigger 待触发,等候一小段时间后再次检查

2. 假如有一个可用的 Trigger,等待触发它的确切时间的到来

    D. 时间到了,为 Trigger 获取到 triggerFiredBundle.

   E. 使用 Scheduler和triggerFiredBundle为Job创建一个JobRunShell实例

    F. 告诉 ThreadPool 可能时运行 JobRunShell.
这个逻辑存在于 QuartzSchedulerThread 的 run() 方法中。


2.QuartzSchedulerResources
   当工厂创建Scheduler实例,它还会传递给Scheduler一QuartzSchedulerResoures实例。 QuartzSchedulerResourecs 除包含以上东西之后,还有一个 ThreadPool 实例,它提供了一个工作者线程池来负责执行 Job.在 Quartz 中,ThreadPool 是由 org.quartz.spi.ThreadPool 接口 (因为 Quartz 是在 JDK 1.5 之前产生的,所以需要自己的 ThreadPool 类确保向后的兼容性,Quartz 仍然用自己的 ThreadPool 替代 Java 的) 表示的,并提供一个名为 org.quartz.simp.SimpleThreadPool 的具体实现类。SimpleThreadPool 有一个固定数目的工作者线程,在加载之后就不再减少或增多。下面是它的一个描述图:

  
  大家可以看出,做为执行多个job的Quartz框架,它非常需要一个线程池来管理各个执行Job的线程;毕竟,系统资源有限;

3.什么是 Quartz 工作者线程?
     Quartz 不会在 main 线程中处理你的 Job。如果这么做,会严重降低应用的可扩展性。相应的,Quartz 把线程管理的职责委托给分散的组件。对于一般的 Quartz 设置 (这部分还是很费功夫的),都是用SimpleThreadPool  类处理线程的管理。SimpleThreadPool 创建了一定数量的 WorkerThread 实例来使得 Job 能够在分散的线程中进行处理。WorkerThread 是定义在 SimpleThreadPool 类中的内部类,它实质上就是一个线程。要创建 WorkerThread 的数量以及为他们的优先级是配置在文件quartz.properties 中并传入工厂的。

    当 QuartzSchedulerThread 请求 ThreadPool (SimpleThreadPool )去运行 JobRunShell 实例,ThreadPool 就检查看是否有一个可用的工作者线程。假如所以已配置的工作者线程都是忙的,ThreadPool 就等待直到有一个变为可用。当一个工作者线程是可用的,并且有一个 JobRunShell 等待执行,工作者线程就会调用 JobRunShell 类的 run() 方法。

4.配置可选择的 ThreadPool
   Quartz 框架允许你改变所用的 ThreadPool 实现。替换类必须实现ThreadPool 接口,但是框架只支持通过在文件中配置的方式改变 ThreadPool 的实现类。例如,你可以使用更为高级的 ThreadPool 实现--随时基于需求改变线程的数量,甚至是从应用服务器中获得工作者线程。对于大多数用户,默认的实现就足够了。

5.JobRunShell 的 run() 方法
     WorkerThread 是基于于 Java 线程,JobRunShell 类基于实现了 Runable。那意味着它可以作为一个线程并包含一个 run() 方法。JobRunShell 的目的是调用 Job 的 execute() 方法。不仅如此,它还要负责通知 Job 和 Trigger 监听器,在运行完成后还得更新此次执行的 Trigger 的信息。大家可以看出了吧,JobRunShell =Runnable,WorkerThread =Thread;他们是一个依附关系,即一个任务需要由一个线程来进行驱动;

阅读(1476) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-11-02 17:35:22

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com