一种是通过实现Runnable接口,另一种是在主方法实现。
一、第一种 实现javalangRunnable接口
循环输出0-99这100个数字。
public class TestThread1{
publicstatic void main(String args[]){
Runner1r = new Runner1(); //new一个线程对象Runner1
//r.start();//通知CPU要启动线程
//r.run();//方法调用
Thread t = new Thread(r); //为了启动Runner1这个线程,需要实例化Thread,并传入自己的Runner1实例
t.start();//通知CPU要启动线程
for(inti = 0; i<100; i++){
System.out.println("MainThread:-----" + i);
}
}
}
class Runner1 implements Runnable{ //实现Runnable接口
//class Runner1 extends Thread{ //本身就是一个Thread,就不用再new一个
publicvoid run(){ //run方法里写的是什么,线程调用时执行的就是什么
for(inti=0; i<100; i++){ //从0-99挨个输出
System.out.println("Runner1:" + i);
}
}
}
运行结果:
运行顺序图:
Runner1类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target)构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
二、第二种 扩展javalangThread类
public class TestThread1{
publicstatic void main(String args[]){
Runner1r = new Runner1(); //new一个线程对象Runner1
r.start();//通知CPU要启动线程
//r.run();//方法调用
//Threadt = new Thread(r);
//t.start();
for(inti = 0; i<100; i++){
System.out.println("MainThread:-----" + i);
}
}
}
//class Runner1 implements Runnable{ //实现Runnable接口
class Runner1 extends Thread{ //本身就是一个Thread,就不用再new一个
publicvoid run(){ //run方法里写的是什么,线程调用时执行的就是什么
for(inti=0; i<100; i++){ //从0-99挨个输出
System.out.println("Runner1:" + i);
}
}
}
publicstatic void main(String args[]){
Runner1r = new Runner1(); //new一个线程对象Runner1
r.start();//通知CPU要启动线程
//r.run();//方法调用
//Threadt = new Thread(r);
//t.start();
for(inti = 0; i<100; i++){
System.out.println("MainThread:-----" + i);
}
}
}
//class Runner1 implements Runnable{ //实现Runnable接口
class Runner1 extends Thread{ //本身就是一个Thread,就不用再new一个
publicvoid run(){ //run方法里写的是什么,线程调用时执行的就是什么
for(inti=0; i<100; i++){ //从0-99挨个输出
System.out.println("Runner1:" + i);
}
}
}
运行结果:
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()方法调用时候被创建。随着调用r的对象的start方法,另外的一个线程也启动了,这样,整个应用就在多线程下运行。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。
使用继承会比较死,从这个类继承之后就不能再从其他的类去继承,但是如果如果用的是接口,若实现了这个接口之后,还可以从其他的类继承,还可以实现其他的接口。所以,从接口来说实现接口会比较灵活,原则是能使用接口的时候不要从Thread类继承。总之,如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易实现资源共享。
PS:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。从程序运行的结果可以发现,多线程程序是乱序执行,所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。因此,只有乱序执行的代码才有必要设计为多线程。
三、方法调用
按调用的顺序执行,为了方便查看,循环输出改为0-9这10个数。
public class TestThread1{
publicstatic void main(String args[]){
Runner1r = new Runner1(); //new一个线程对象Runner1
//r.start();//通知CPU要启动线程
r.run();//方法调用
//Threadt = new Thread(r);
//t.start();
for(inti = 0; i<10; i++){
System.out.println("MainThread:-----" + i);
}
}
}
//class Runner1 implements Runnable{ //实现Runnable接口
class Runner1 extends Thread{ //本身就是一个Thread,就不用再new一个
publicvoid run(){ //run方法里写的是什么,线程调用时执行的就是什么
for(inti=0; i<10; i++){ //从0-9挨个输出
System.out.println("Runner1:" + i);
}
}
}
publicstatic void main(String args[]){
Runner1r = new Runner1(); //new一个线程对象Runner1
//r.start();//通知CPU要启动线程
r.run();//方法调用
//Threadt = new Thread(r);
//t.start();
for(inti = 0; i<10; i++){
System.out.println("MainThread:-----" + i);
}
}
}
//class Runner1 implements Runnable{ //实现Runnable接口
class Runner1 extends Thread{ //本身就是一个Thread,就不用再new一个
publicvoid run(){ //run方法里写的是什么,线程调用时执行的就是什么
for(inti=0; i<10; i++){ //从0-9挨个输出
System.out.println("Runner1:" + i);
}
}
}
运行结果:
运行顺序图:
按调用方法的顺序执行,先子线程执行完了,主线程才开始执行。
输出的时候根据main方法里调用的顺序,会先执行r.run()方法里的循环体,打印输出为"Runner1:" + i,当执行到main方法里的循环体的时候再次打印输出,输出为"MainThread:-----" + i。
四、线程与进程的区别
简单理解线程与进程:
线程指的是程序里不同的执行路径,看上述的运行顺序图,机器上实际上运行的是线程;
进程是一个静态的概念,一个进程里有一个主线程叫做main()方法,是一个程序、一个进程里面的不同的执行路径。
区别:
1.每个进程都有独立的代码和数据空间,进程的切换会有较大的开销
2.线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程的切换开销小。