1.2 线程的创建与运行
在Java语言中,一个线程就是一个java.lang.Thread类的实例。因此,在Java语言中创建一个线程就是创建一个Thread类的实例,当然这离不开内存的分配。创建一个Thread实例与创建其他类的实例不同的是,Java虚拟机会为一个Thread实例分配两种调用栈(Call Stack)所需的内存空间。这两种调用栈一种用于跟踪Java代码间的调用关系,另一种用于跟踪Java代码与本地代码(即Native代码,通常是C代码)的调用关系[2]。
一个Thread实例可能对应两个线程:一个是Java虚拟机中的线程(或称之为Java线程),另一个是与Java虚拟机中的线程相对应的依赖于Java虚拟机宿主机操作系统的本地(Native)线程[3]。启动一个Java线程只需要调用相应Thread实例的start方法即可。线程启动后,当相应的线程被线程调度器调度至运行时,相应Thread实例的run方法就会被Java虚拟机调用,如清单1-2所示[4]。
清单1-2 Java线程的创建与运行
在清单1-2中,我们通过新建一个Thread类实例来创建一个线程。Thread类的其中一个构造器支持传入一个java.lang.Runnable接口实例,当相应线程运行时,该实例的run方法会被Java虚拟机调用。
如清单1-2所示的程序运行时输出如下:
与清单1-1中的代码相比,同样的类Helper的同一个方法doSomething此时是由名为A-Worker-Thread的线程而非main线程负责执行的。这是因为在清单1-1中,类Helper的run方法由main线程所执行的main方法直接调用,而在清单1-2中,类Helper的run方法是由Java虚拟机通过其创建的线程(线程名为A-Worker-Thread)进行调用的。
在清单1-2中,对线程对象的start方法进行调用(thread.start())的这段代码是运行在main方法中的,而main方法是由main线程负责执行的。因此,在这里我们所创建的线程thread就可以看成main线程的一个子线程,而main线程就是该线程的父线程。
在Java语言中,子线程是否是一个守护线程取决于其父线程:默认情况下父线程是守护线程则子线程也是守护线程,父线程是用户线程则子线程也是用户线程。当然,父线程在创建子线程后和启动子线程前可以调用Thread实例的setDaemon方法来修改线程的这一属性。
Thread类自身是一个实现java.lang.Runnable接口的对象,我们也可以通过定义一个Thread类的子类来创建线程,自定义的线程类要覆盖其父类的run方法,如清单1-3所示。
清单1-3 以创建Thread子类的方式创建线程