IT Notes‎ > ‎Java‎ > ‎Java Language‎ > ‎

Java 线程基本介绍

按:本文的大量内容来源于文末所附的参考页面。百度百科的那篇对普通线程的描述较好,IBM 文库那篇对 Java 线程的原理介绍较好。

1. 什么是线程

线程(Thread)是程序执行流的最小单元,在台湾也被称为「执行绪(執行緒)」,这个名称显然更加直观。线程有时被称为轻量级进程(Lightweight Process, LWP)
  1. 一个标准的线程由线程 ID,当前指令指针(PC),寄存器集合和堆栈组成。
  2. 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
  3. 一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。
  4. 线程有就绪、阻塞和运行三种基本状态。
  5. 每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
注:这几点来自百度百科的相关信息整理

2. Java 线程

Java 线程,本质上对应操作系统的本地线程(native thread),两者是一一对应的。Java 语言提供线程编程接口。在使用这些接口时,本质上也是对操作系统的线程进行操作,这里仅从 Java 的视角去讨论线程。

2.1 创建线程

Java 线程机制提供了一个统一的线程函数,该线程函数通过 Java 虚拟机调用 Java 线程方法 , 这是通过 Java 本地方法调用来实现的。

how to create a java thread?
图:Java 线程创建调用关系图,参1
Java 线程的创建调用过程如上图所示,首先 , Java 线程的 start 方法会创建一个本地线程(通过调用 JVM_StartThread),由其再进一步调用 run 方法。Java 线程的 run 方法和普通方法其实没有本质区别,直接调用 run 方法不会报错,但是却是在当前线程执行,而不会创建一个新的线程。

可以通过以下两种方式创建线程实例:
1. 扩展 Thread 类
public class HelloThread1 extends Thread {

        @Override
        public void run() {
              System.out.println("Thread started!" );
       }

        public static void main(String[] args) {
              Thread t = new HelloThread1();
              t.start();
       }

}

2. 实现 Runnable 接口
public class HelloThread2 implements Runnable {

        @Override
        public void run() {
              System. out.println("Thread invoked!" );
       }

        public static void main(String[] args) {
              Thread t = new Thread(new HelloThread2());
              t.start();
       }

}

不管是用哪种方法创建线程,实际上都是要实现一个 run 方法的。 run 方法本质是上一个回调方法,由 start 方法新创建的线程会调用这个方法从而执行需要的代码。 run 方法并不是真正的线程函数,只是被线程函数调用的一个 Java 方法而已,和其他的 Java 方法没有什么本质的不同。这也回答了「为何新起线程,要调用 start 方法,而不能调用 run 方法?」的问题。

使用接口创建线程在编程实践中更为灵活,也用的最多,是通常情况下推荐的用法。

3. Java 线程与操作系统线程

Java 线程是建立在系统本地线程之上的,是另一层封装。Java 的线程状态,比 Windows/Linux 这些 OS 的线程状态要简单很多。其面向 Java 开发者提供的接口存在以下的局限性:
    1. Java 线程机制不能获取线程退出的返回值,因而无法检测线程是正常退出还是异常终止。
    2. 线程的同步。Java 提供方法 Thread.join() 来等待一个线程结束,一般情况这就足够了,但一种可能的情况是,需要等待在多个线程上(比如任意一个线程结束或者所有线程结束才会返回),循环调用每个线程的 Join 方法是不可行的,这可能导致很奇怪的同步问题。
    3. Java 线程机制不能提供真实的线程 ID。Thread.getID() 的返回值是一个简单的计数 ID,和操作系统的线程 ID 没有任何关系。
    4. Java 没有提供方法来获取线程中某段代码的运行时间的统计结果。虽然可以自行使用计时的方法来实现(获取运行开始和结束的时间,然后相减 ),但由于存在多线程调度方法的原因,无法获取线程实际使用的 CPU 运算时间,因而必然是不准确的。

参考:



Comments