搜索
写经验 领红包

线程的创建和启动的区别(线程的创建方式)

导语:线程的创建和启动

Java虚拟机启动时会创建一个主线程,它从启动类的main()方法开始执行。此外,用户还可以创建自己的线程,他将和主线程并发运行。创建方式有两种:

扩展java.lang.Thread类实现Runnable接口扩展java.lang.Thread类

Thread类代表线程类,它主要有两个方法:

run():包含线程运行时所执行的代码start():用于线程启动

用户的线程只要继承Thread类,覆盖Thread类的run()方法。在Thread类中run()方法定义如下:

public void run()

这个方法没有抛出任何异常,根据方法覆盖的规则,Thread子类的run()方法也不能声明抛出任何异常。看下面用户自定义的线程类,在run()方法中指定了这个线程所执行的代码:

public class Machine extends Thread {

public void run() {

for(int a=0;a<50;a++){

System.out.println(a);

}

}

public static void main(String[] args) {

Machine machine=new Machine();

machine.start(); //启动线程

}

}

当运行java Machine命令时,Java虚拟机首先创建并启动主线程,主线程的任务是执行main()方法,main方法()创建了一个Machine对象,然后调用它的start()方法启动Machine线程。Machine线程的任务是执行它的run()方法。

通过下面实例,了解线程的运行过程、Thread类的start()方法用法。

示例一:主线程和用户自定义的线程并发运行

public class Machine extends Thread {

public void run() {

for(int a=0;a<50;a++){

System.out.println(currentThread().getName()+":"+a);

try{

sleep(100);//给其他线程运行的机会

}catch(InterruptedException e){

throw new RuntimeException(e);

}

}

}

public static void main(String[] args) {

Machine machine1=new Machine();

Machine machine2=new Machine();

machine1.start();

machine2.start();

machine1.run();//主线程执行第一个machine1的run()方法

}

}

当主线程执行main()方法时,会创建两个Machine对象,然后启动两个Machine线程,接着主线程开始执行第一个machine对象的run()方法。在虚拟机中,有3个线程并发执行Machine对象的run()方法。在3个线程各自的方法栈中都有代表run()方法的栈桢,在这个桢中存放了局部变量a,因此每个线程都有自己的局部变量a。

上面代码中,Machine类的run方法里的currentThread().getName()相当于一下代码:

Thread thread =Thread.currentThread();

String name=thread.getName();

Thread类的currentThread()静态方法返回当前线程的引用,Thread类的getName()方法返回线程的名字。每个线程都有默认名字,主线程默认名字是“main”,用户创建的第一个线程默认名字是“Thread-0”,第二个线程默认名字是“Thread-1”,依此类推。Thread类的setName()方法可以设置自定义的线程名字。

为了让每个线程轮流获得CPU,在run()方法中还调用了Thread类的sleep()静态方法,该方法让当前线程放弃CPU,并睡眠指定长时间。

多线程共享一个对象的实例变量

public class Machine extends Thread {

private int a=0;//实例变量

public void run() {

for(a=0;a<50;a++){//使用实例变量a

System.out.println(currentThread().getName()+":"+a);

try{

sleep(100);//给其他线程运行的机会

}catch(InterruptedException e){

throw new RuntimeException(e);

}

}

}

public static void main(String[] args) {

Machine machine=new Machine();

machine.start();//启动一个Machine的线程

machine.run();//主线程执行run()方法

}

}

上面代码,在Machine类中,变量a是一个实例变量,Machine的run()方法调用这个变量a。

运行以上程序,主线程和Machine线程都会执行Machine对象的run()方法。下图显示了主线程和Machine线程并发运行时的运行数据区

我的机器上运行结果:

main:0

Thread-0:0

main:1

Thread-0:2

main:3

Thread-0:4

...

main:46

Thread-0:47

main:48

Thread-0:49

如果对上面的示例代码做修改:

public static void main(String[] args) {

Machine machine1=new Machine();

Machine machine2=new Machine();

machine1.start();

machine2.start();

}

我机器上运行结果:

Thread-0:0

Thread-1:0

Thread-1:1

Thread-0:1

...

Thread-0:49

Thread-1:49

这是因为machine1线程和machine2线程分别执行machine1对象和machine2对象的run()方法,当machine1线程执行run()方法时,会把方法区中run()方法中的变量a解析为machine1对象的实例变量a;machine2线程执行run()方法时,会把方法区中run()方法的变量a解析为machine2对象的实例变量a,因此machine1线程和machine2线程分别操作不同的实例变量a。

不要随便覆盖Thread类的start()方法

创建一个线程后,线程并不会自定执行,需要调用start()方法后才能启动线程。JDK中Thread类的start()方法的默认实现:

public synchronized void start() {

if (threadStatus != 0)

throw new IllegalThreadStateException();

group.add(this);

boolean started = false;

try {

start0();

started = true;

} finally {

try {

if (!started) {

group.threadStartFailed(this);

}

} catch (Throwable ignore) {

}

}

}

当new语句创建一个线程时,仅仅是在堆区中创建了一个对象,此时该线程还没有被启动。当线程的start()方法被执行时,会启动线程,在Java栈区为它创建相应的调用栈。

假如要覆盖start()方法,那么要在方法的第一行调用super.start()。比如:

public class Machine extends Thread {

public void start(){

super.start();

}

public void run() {

...

}

}

一个线程只能被启动一次

一个线程只能被启动一次,否则会报错:

Machine machine=new Machine();

machine.start();

machine.start();

第二次调用machine.start()方法时,会抛出java.lang.IllegalThreadStateException异常

实现Runnable接口

我们知道继承不能继承多个类,因此一旦一个类继承了Thread类,就不能再继承其他的类。为了解决这一问题,Java提供了java.lang.Runnable接口,它有个run()方法,定义如下:

public void run();

下面是一个实现了Runnable接口的示例代码

public class Machine implements Runnable {

private int a=0;//实例变量

public void run() {

for(a=0;a<50;a++){//使用实例变量a

System.out.println(Thread.currentThread().getName()+":"+a);

try{

Thread.sleep(100);//给其他线程运行的机会

}catch(InterruptedException e){

throw new RuntimeException(e);

}

}

}

public static void main(String[] args) {

Machine machine=new Machine();

Thread t1=new Thread(machine);

Thread t2=new Thread(machine);

t1.start();

t2.start();

}

}

在Thread类中定义了如下形式的构造方法:

主线程创建了t1和t2两个线程对象。当启动t1和t2线程时,都会执行machine变量所引用的Machine对象的run()方法。t1和t2共用一个machine对象,因此在执行run()方法时,操作的是同一个实例变量a,以上程序打印结果:

Thread-0:0

Thread-1:0

Thread-0:1

Thread-1:2

...

Thread-1:47

Thread-0:48

Thread-1:49

目录:Java知识目录

本文内容由小涵整理编辑!