Skip to content

多线程调用

Java 中 JVM 允许程序运行多个线程,使用 java.lang.Thread 类代表 线程,所有的线程对象都必须是 Thread类或子类的实例。

Thread 类的特点:

  • 每个线程都必须重写Thread对象的run()方法来完成执行,因此run()也叫做线程执行体
  • 线程需要通过Thread对象的start()方法来启动该线程,而非直接调用run()方法;
  • 想要实现多线程,必须在主线程(main方法)中创建新的线程对象,而非多次调用start()

创建线程的方式

继承Thread类

Thread 类有四个构造器,后续都会讲到并使用:

构造器作用
public Thread()创建一个新的线程对象
public Thread(String name)创建一个指定名字的新的线程对象
public Thread(Runnable target)创建一个新的线程的目标对象,它实现了 Runnable 接口,并重写了 run 方法
public Thread(Runnable target, String name)创建一个新的线程的目标对象,并指定名字

通过继承Thread类来 创建启动多线程

java
class PrintfNumber extends Thread {
  //通过构造函数为当前线程指定名称
  public PrintfNumber(String name) {
    super(name);
  }

  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + "hello");
  }
}
java
public static void main(String[] args) {
  PrintfNumber p1 = new PrintfNumber("Thread1");
  p1.start();
}

其中 mian() 方法可以理解为主线程,而自定义的 PrintfNumber 就是一个分线程,运行在主线程中。

拓展写法

匿名线程 是一种特殊的线程创建方式,它不需要显式地定义一个线程类,而是直接在需要的地方创建一个线程对象。

java
//匿名线程
new Thread() {
  @Override
  public void run() {
    System.out.println("Hello from anonymous thread!");
  }
}.start();

//命名线程
new Thread("thread") {
  @Override
  public void run() {
    System.out.println("Hello from anonymous thread!");
  }
}.start();

实现Runnable接口

Java中有单继承的限制,第一种方式当我们继承了Thread类后,就无法继承其他类了。

Java核心类库中提供了 Runnable 接口,我们可以继承 Runnable 接口,规避上面无法继承其他类的情况。

java
class EvenNumberThread implements Runnable {
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + "hello");
  }
}
java
public static void main(String[] args) {
  EvenNumberThread e = new EvenNumberThread();
  //这时需要通过 Thread() 进行调用, Thread()可以接受一个Runnable对象
  new Thread(e).start();
}

拓展写法

java8 之后,新增了 lambda 语法糖写法:

java
//Thread()中传入一个 Runnable 对象
new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println("Hello from anonymous thread!");
  }
}).start();

//简化写法
new Thread(() -> System.out.println("Hello from anonymous thread!")
  .start();

线程常用方法

方法描述
currentThread()获取当前执行代码的线程
getName()获取当前线程的名字
setName()设置当前线程的名字
sleep(1000)使线程睡眠指定的时长(单位:毫秒)
yield()执行该方法,表示当前线程将释放CPU的执行权
join()子线程调用该方法,表示主线程会阻塞,直到子线程执行完成后,主线程再执行
isAlive()判断当前线程是否存活
java
public static void main(String[] args) {
  EvenNumber e1 = new EvenNumber();
  e1.start();

  for (int i = 1; i <= 100; i++) {
    if (i % 20 == 0) {
      //主线程释放CPU执行权,让子线程优先执行
      Thread.yield();
    }
  }
}

class EvenNumber extends Thread {
  @Override
  public void run() {
    for (int i = 1; i <= 100; i++) {
      System.out.println(i);
    }
  }
}
java
public static void main(String[] args) throws InterruptedException {
  EvenNumber e2 = new EvenNumber();
  e2.start();

  for (int i = 1; i <= 100; i++) {
    if (i % 20 == 0) {
      //主线程将阻塞,把运行权给到分线程,当分线程执行完成后主线程再执行
      e2.join();
    }
  }
}

class EvenNumber extends Thread {
  @Override
  public void run() {
    for (int i = 1; i <= 100; i++) {
      System.out.println(i);
    }
  }
}

线程优先级

每个线程都有一定的优先级,相同优先级的线程使用分时调度策略,优先级高的线程采用抢占式策略,获得较多的执行机会。

设置线程优先级方法:

方法描述
getPriority获取当前线程优先级
setPriority()设置当前线程优先级,取值范围 [1, 10] 之间

Java自带的线程优先级常量:

常量描述
MIN_PRIORITY最小线程级别,值为 1
NORM_PRIORITY默认线程级别,值为 5
MAX_PRIORITY最大线程级别,值为 10
java
public class PriorityTest {
  public static void main(String[] args) {
    EvenNumber e4 = new EvenNumber();
    //为子线程设置最大线程级别
    e4.setPriority(Thread.MAX_PRIORITY);
    e4.start();

    //为main主线程,设置最小线程级别
    Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
    for (int i = 1; i <= 100; i++) {
      System.out.println(Thread.currentThread().getPriority() + ":" + i);
    }
  }
}

class EvenNumber extends Thread {
  @Override
  public void run() {
    for (int i = 1; i <= 100; i++) {
      System.out.println(Thread.currentThread().getPriority() + ":" + i);
    }
  }
}

Released under the MIT License.