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++){
Sy(a);
}
}
public static void main(String[] args) {
Machine machine=new Machine();
mac(); //启动线程
}
}
当运行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++){
Sy(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();
mac();
mac();
mac();//主线程执行第一个machine1的run()方法
}
}
当主线程执行main()方法时,会创建两个Machine对象,然后启动两个Machine线程,接着主线程开始执行第一个machine对象的run()方法。在虚拟机中,有3个线程并发执行Machine对象的run()方法。在3个线程各自的方法栈中都有代表run()方法的栈桢,在这个桢中存放了局部变量a,因此每个线程都有自己的局部变量a。
上面代码中,Machine类的run方法里的currentThread().getName()相当于一下代码:
Thread thread =T();
String name=();
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
Sy(currentThread().getName()+":"+a);
try{
sleep(100);//给其他线程运行的机会
}catch(InterruptedException e){
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
Machine machine=new Machine();
mac();//启动一个Machine的线程
mac();//主线程执行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();
mac();
mac();
}
我机器上运行结果:
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()方法,那么要在方法的第一行调用()。比如:
public class Machine extends Thread {
public void start(){
();
}
public void run() {
...
}
}
- 一个线程只能被启动一次
一个线程只能被启动一次,否则会报错:
Machine machine=new Machine();
mac();
mac();
第二次调用mac()方法时,会抛出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
Sy(T().getName()+":"+a);
try{
T(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);
();
();
}
}
在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知识目录