创建和启动线程

概述

Java语言的JvM允许程序运行多个线程,使用 java.1ang.Thread 类代表 线程,所有的线程对象都必须是
Thread类或其子类的实例。

Thread类的特性

  • 每个线程都是通过某个特定Thread对象的 run() 方法来完成操作的,因此把 run() 方法体称为 线程执行体
  • 通过该Thread对象的start)方法来启动这个线程,而非直接调用 run()
  • 要想实现多线程,必须在主线程中创建新的线程对象。

Thread().start() 会调用 run() 方法,在源码中会调用 start0() 一个 native 的方法,后面是操作系统发挥作用。

方式1:继承 Thread 类

创建一个继承 Thread 的子类
重写 Thread 类的 run() ,将此线程要做的操作声明在此方法体中
创建当前 Thread 的子类的对象
通过对象调用 start() 方法,start() 的两个作用,一个是启动线程一个是调用 run() 方法

问题1:是否可以直接调用 run() 替换 start() 的调用,实现分线程的调用?
使用 run() 的调用程序可以运行,但是从整体看此时就不满足并发发生的情景,而是单线程发生的场景。

问题2:基于同一个对象再次运行 start() 方法,是否可以再次创建一个新的线程?
不会,从下面的代码中可以看到此时会报一个异常
if (holder.threadStatus != 0)
   throw new IllegalThreadStateException();
要开启一个新的线程,就要创建一个新的线程对象。

示列:

class PrintNumber extends Thread{
    public void run(){
        for (int i = 1; i <= 10000; i++) {
            if (i % 2 != 0){
                System.out.println(i);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        PrintNumber t1 = new PrintNumber();
        t1.start();

        for (int i = 1; i < 10000; i++) {
            if( i % 2!= 0 ){
                System.out.println(i + "是一个奇数 main");
            }
        }
    }
}

==>
--snip--
543
8545
8547
8549
8551
8553
3439是一个奇数 main
3441是一个奇数 main
3443是一个奇数 main
3445是一个奇数 main
3447是一个奇数 main
3449是一个奇数 main
3451是一个奇数 main
3453是一个奇数 main
3455是一个奇数 main
3457是一个奇数 main
8555
8557
8559
8561
--snip--

PrintNumber 是一个继承 Thread 的类,Main类中实例化 PrintNumber 类,在主类中也创建一个线程任务。

Thread.currentThread().getName()
这个方法用于打印当前线程的名字。


方式2:实现 Runnable 接口

由于继承Thread类具有一定的局限性(java的单继承不能继承其他的类),这就需要使实现 Runnable() 接口的方式。

创建一个实现Runnable接口的类
实现接口中的run()-->将此线程要执行的操作,声明在此方法体中
创建当前实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
Thread类的实例调用 start()

为什么 new Thread(Runnable对象).run() 调用的是Runnable对象的方法。而没有调用 Thread对象的run()方法?
对于 Thread类:
private Runnable target;
public void run(){
  if (targrt != null){
   targrt.run();
  }
}

class EvenNumberPrint implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        EvenNumberPrint evenNumberPrint = new EvenNumberPrint();
        new Thread(evenNumberPrint).start();

        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

==>
--snip--
main75
Thread-084
main77
Thread-086
main79
Thread-088
main81
main83
--snip--

方式3:匿名

使用匿名实现类的匿名对象。

new Thread(new Runnable() {
    @Override
    public void run() {

    }
}).start();

对比两种方式

共同点:

  • ① 启动线程使用的都是 Thread 类中定义的方法
  • ② 创建的线程对象都是 Thread 类或其子类的实例。

不同点:

  • 一个是类的继承,一个是接口的实现

建议使用 Runnable 接口的方式,好处:实现的方式避免类单继承的实现性。更适合处理有共享数据的问题,如 evenNumberPrint 对象可以被其他的 Thread 类作为参数共享。

联系:

public class Thread implemnets Runnable

代理模式。

posted @ 2025-07-09 23:14  takenika  阅读(15)  评论(0)    收藏  举报