Runnable 接口是 Java 中用于创建线程的基础接口之一,它定义了一个无参无返回的方法 run(),而在我们的认知中,其是创建线程的方法之一,使得对象可以在线程中执行,而无需继承Thread类。
一、Runnable 接口如何“新建”线程
事实上,这个所谓的“新建”只是一个通俗的叫法,就Java而言,代表线程的类只有Thread一种。该类在执行start方法启动线程时,JVM会调用该线程的run方法,我们通过继承Thread类来新建多线程任务时,往往也是重写run方法来实现一部分逻辑,但是,Thread源码中的run方法实际上是这样的:
public void run() {
if (target != null) {
target.run();
}
}
也就是说,使用Runnable 接口“新建”线程,实际上就是调用Thread的构造函数,将实现了Runnable 接口的对象传入,并赋值给target属性,如此一来,JVM调用该线程的run方法,也就调用了实现Runnable接口的对象的run方法。
二、使用Runnable 接口“新建”线程的意义
从逻辑上而言,该方法是为了将线程本身的控制逻辑和业务逻辑的分离,当然,继承Thread也能一定程度上实现这点,但继承Thread类实现多线程存在一个很大的不足,那就是多线程资源的共享。
众所周知,一个Thread对象只能start一次,我们如果想要让多个线程同时运行,就要新建多个Thread对象,那么,如何我们想让这些Thread对象共享同一份资源呢?最简单的方式,我们将资源设为静态变量,但是,这样一来,其生存周期就大大延长,若共享资源很多且需要复杂的逻辑,就更加麻烦。如此一来,我们反而不如将共享资源独立出来,也就是说,向不同的Thread对象传入同一个变量,由此实现资源的共享,如此考虑,就需要保证run方法中可以取到该变量,如果我们不想每次都通过匿名实现类新建传参,最好的实现方式就是将该变量传入作为Thread对象的一个属性,变量已经传入了,我们总不能每次都根据变量的不同重写run方法吧,那样代码逻辑的耦合度也太高了,这么一看,反而不如将run方法的实现也交给该变量来实现。
为了保证该变量能够实现run方法,便提出个接口规范吧:
private Runnable target;//保证符合Runnable接口规范的才能传入
在此情况下,Thread类只需要完成控制逻辑,其它的都托管给“Runnable类”完成。
三、使用举例 -- 作为参数传入到Thread的构造方法
- 普通传入
public class Worker implements Runnable {
@Override
public void run() {
// 业务逻辑
System.out.println("Executing worker task...");
}
}
public class Main {
public static void main(String[] args) {
Runnable worker = new Worker();
Thread thread = new Thread(worker);
thread.start();
}
}
- 线程池传入
public class Worker implements Runnable {
@Override
public void run() {
// 业务逻辑
System.out.println("Executing worker task...");
}
}
public class Main {
public static void main(String[] args) {
Runnable worker = new Worker();
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {//多个线程执行同一套逻辑,正常情况下需要考虑加锁
executorService.execute(worker);
}
executorService.shutdown();
}
}
四、总结
通过实现Runnable接口的对象传递共享资源和业务逻辑,实际上实现了的线程控制和业务执行解耦分离,是一种简单而使用高效的实现多线程的方式。
标签:Runnable,run,Thread,接口,线程,public From: https://www.cnblogs.com/bbban/p/18404855