Java并发_开启线程(1)

Thread 类

查看 Thread 类源码可知,其实现了 Runnable 接口并重写了 run 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Thread implements Runnable {
  //在Thread类加载时就注册本地方法,例如start0方法
  private static native void registerNatives();
  static {
    registerNatives();
  }
  //调用start方法会开启一个新的线程
  public synchronized void start() {
    start0();
    //省略... 
  }
  private native void start0();

  public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
  }

  private Runnable target;
  //用传入的Runnable实现对象来构造
  public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
  }

  //在开启的新线程中,JVM会调用run方法
  @Override
  public void run() {
    //如果传入了Runnable任务,则调用其重写的run方法
    if (target != null) {
      target.run();
    }
  }
}

在 java 代码层调用 start 方法开启一个线程,实际上是调用了 native 方法 start0 ,查看 JDK 源码可知,在 Thread.c 中定义了与 start0 方法对应的 JVM_StartThread 函数的指针

stop、sleep 等方法同理

1
2
3
4
5
6
7
//jdk\src\share\native\java\lang\Thread.c
static JNINativeMethod methods[] = {
  {"start0",           "()V",        (void *)&JVM_StartThread},
  {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
  {"sleep",            "(J)V",       (void *)&JVM_Sleep},
  //省略...
};

JVM_StartThread 函数定义在 jvm.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//hotspot\src\share\vm\prims\jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;
  //省略...
  {
    //创建一个线程并传入线程函数thread_entry
    native_thread = new JavaThread(&thread_entry, sz);
    //省略...
  }

//线程函数thread_entry
static void thread_entry(JavaThread* thread, TRAPS) {
  vmSymbols::run_method_name(),
  //省略...
}

JavaThread 是 JVM 层抽象的平台无关线程,定义在 thread.cpp

1
2
3
4
5
6
7
8
//hotspot\src\share\vm\runtime\thread.cpp
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  //省略...
{
  //省略...
  //调用具体系统创建线程的方法来创建线程
  os::create_thread(this, thr_type, stack_sz);
}

thread_entry 调用的 run_method_name 定义在 vmSymbols.hpp

1
2
//hotspot\src\share\vm\classfile\vmSymbols.hpp 
template(run_method_name, "run")

所以开启一个线程并执行任务,只需要将要执行的代码重写进 run 方法中,然后调用 start 方法即可

开启线程执行任务

重写 Thread 类的 run 方法

可以定义一个类继承 Thread 类并重写 run 方法,直接将要执行的任务写入 run 方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Test {
  public static void main(String[] args) {
    //默认线程名称为Thread-线程号
    MyThread t1 = new MyThread();
    //可以通过构造方法字自定义线程名
    MyThread t2 = new MyThread("线程2");
    MyThread t3 = new MyThread("线程3");
    //手动调用run方法不会开启新的线程,而是由main线程执行
    //t1.run();
    t1.start();
    //线程只能启动一次,多次启动会报异常IllegalThreadStateException
    //t1.start();
    t2.start();
    //还可以通过setName方法给线程改名
    t3.setName("线程333");
    t3.start();
    for (int i = 0; i < 3; i++) {
      //Thread的静态native方法currentThread获取当前正在执行的线程
      System.out.println(Thread.currentThread().getName() + "执行");
    }
  }
}
class MyThread extends Thread {
  MyThread() {}
  MyThread(String name) {
    super(name);
  }
  public void run() {
    for (int i = 0; i < 1; i++) {
      System.out.println(this.getName() + "执行");
    }
  }
}
//main执行
//main执行
//main执行
//Thread-0执行
//线程2执行
//线程333执行

Thread.currentThread 返回的是当前的线程,而 Thread 对象的 this 就只是返回的对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    //将t1的run方法交给t2执行
    //这时t1就仅仅是一个任务对象
    Thread t2 = new Thread(t1);
    t2.start();
  }
}
class MyThread extends Thread {
  public MyThread() {
    System.out.println(
      "在" + Thread.currentThread().getName() + "中构造" + this.getName() + "对象");
  }
  @Override
  public void run() {
    System.out.println(
      "在" + Thread.currentThread().getName() + "中执行" + this.getName() + "的run方法");
  }
}
//在main中构造Thread-0对象
//在Thread-1中执行Thread-0的run方法

传入 Runnable 接口实例

1
2
3
public interface Runnable {
  public abstract void run();
}

Thread 类有一个可以接收 Runnable 接口实例的构造方法,如果传入了 Runnable 接口实例,则执行在实例中定义的 run 方法,可以将 Runnable 接口实例理解为一个任务,交给一个线程去执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
  public static void main(String[] args) {
    //创建Runnable任务
    MyRunnable mr = new MyRunnable();
    //开启一个线程并传入任务
    Thread t = new Thread(mr);
    t.start();
    for (int i = 0; i < 2; i++) {
      System.out.println(Thread.currentThread().getName() + "执行");
    }
  }
}
class MyRunnable implements Runnable {
  @Override
  public void run() {
    for (int i = 0; i < 2; i++) {
      System.out.println(Thread.currentThread().getName() + "执行");
    }
  }
}
//main执行
//main执行
//Thread-0执行
//Thread-0执行

如果需要重写增强 Thread 类提供的方法,才选择继承 Thread 类直接重写其 run 方法的方式,但 Java 是单继承的,所以多数情况使用传入 Runnable 接口实例的方式会更好

传入 FutureTask 实例

因为 Runnable 接口的 run 方法被设计为返回 void,所以继承 Thread 类重写 run 方法或直接传入一个 Runnable 接口的实例,这两种方式都不能获取线程执行的结果

在 JUC 包下定义了一个带有返回结果且能抛出异常的 Callable 接口

1
2
3
public interface Callable<V> {
  V call() throws Exception;
}

只是 Callable 返回的结果或抛出的异常需要通过同在 JUC 包下的 Future 接口来获取

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public interface Future<V> {
  //取消任务,取消成功返回true否则返回false,如果取消的是已完成的任务则也返回false
  //如果任务还在等待则返回true
  //如果任务正在执行了,则要判断传入的参数,即是否打断正在执行的任务
  boolean cancel(boolean mayInterruptIfRunning);
  //判断任务在完成前被成功取消
  boolean isCancelled();
  //判断任务已完成
  boolean isDone();
  //获取执行结果,阻塞的,直到任务执行完才返回
  V get() throws InterruptedException, ExecutionException;
  //获取执行结果,设置超时时间,超时了直接返回null
  V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;
}

Future 的实现类 FutureTask 直接实现的是 RunnableFuture 接口,而 RunnableFuture 接口则继承了 Runnable 和 Future 两个接口,所以 FutureTask 即可以控制 Callable 的 call 方法,自己本身也可以看作是一个 Runnable 任务,即也可以传入 Thread 并开启线程执行

1
2
3
public interface RunnableFuture<V> extends Runnable, Future<V> {
  void run();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class FutureTask<V> implements RunnableFuture<V> {
  //可以传入一个Callable任务交给Future控制
  public FutureTask(Callable<V> callable) {
    if (callable == null)
      throw new NullPointerException();
    this.callable = callable;
    this.state = NEW; //新创建的FutureTask状态为NEW
  }
  //也可以传入一个Runnable任务
  public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW; //新创建的FutureTask状态为NEW
  }
}

对于 Thread 来看 FutureTask 是一个 Runnable 任务,由 JVM 调用其 run 方法,而 FutureTask 的 run 方法实际上是调用了 Callable 的 call 方法,然后将执行结果或异常保存起来,由 get 方法返回

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//FutureTask类
public void run() {
  //省略...
  Callable<V> c = callable;
  if (c != null && state == NEW) {
    V result;
    boolean ran;
    try {
      //调用Callable的call方法
      result = c.call();
      ran = true;
    } catch (Throwable ex) {
      result = null;
      ran = false;
      setException(ex); //保存call方法抛出的异常
    }
    if (ran)
      set(result);      //保存call方法的执行结果
  }
  //省略...
}

在 call 方法的执行过程中会有状态的转换,Future 对 callable 的控制就依赖这些状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/**
 * 任务执行完成,正常返回
 * NEW -> COMPLETING -> NORMAL
 * 任务执行完成,抛出异常
 * NEW -> COMPLETING -> EXCEPTIONAL
 * 还未执行的任务被取消
 * NEW -> CANCELLED
 * 正在执行的任务被打断
 * NEW -> INTERRUPTING -> INTERRUPTED
 */
private volatile int state;
//新创建的FutureTask的状态为NEW
private static final int NEW          = 0;
private static final int COMPLETING   = 1;
private static final int NORMAL       = 2;
private static final int EXCEPTIONAL  = 3;
private static final int CANCELLED    = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED  = 6;

Thread 执行 FutureTask 对象的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {
  public static void main(String[] args) {
    //创建一个有返回值的任务
    Callable<String> callable = new MyCallable();
    //让Future控制callable任务并包装为可传入Thread的RunnableFuture
    RunnableFuture<String> task = new FutureTask<>(callable);
    //开启线程并传入任务
    new Thread(task).start();
    String result = null;
    try {
      //调用Future回调的get方法获取执行结果,会阻塞主线程
      result = task.get();
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println(result); //Callable
  }
}
class MyCallable implements Callable<String> {
  @Override
  public String call() throws Exception {
    //int i = 10 / 0;
    return "Callable";
  }
}

FutureTask 还可以将传入的 Runnable 转换为 Callable

1
2
3
4
5
//FutureTask类
public FutureTask(Runnable runnable, V result) {
  this.callable = Executors.callable(runnable, result);
  this.state = NEW;
}
1
2
3
4
5
6
//Executors类
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

在 Executors 类中定义的静态内部类 RunnableAdapter 是 Callable 接口的实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//Executors类
static final class RunnableAdapter<T> implements Callable<T> {
  final Runnable task;
  final T result;
  RunnableAdapter(Runnable task, T result) {
    this.task = task;
    this.result = result;
  }
  //call方法即调用传入的Runnable的run方法,将传入的result返回
  public T call() {
    task.run();
    return result;
  }
}

所以对于不用返回结果但又想使得任务可以取消、可以跟踪任务状态,可以将任务定义为 Runnable,传入 FutureTask 中让其包装为 Callable 任务,返回值传 null 即可

1
Future<?> f = new FutureTask<Void>(runnable, null);

以上内容是玉山整理的笔记,如有错误还请指出