Java多线程基础类和接口

我们了解了多线程的基本概念,那么在 Java 中,是如何表示线程和使用多线程的呢?

Java 中用 Thread 类来表示线程,提供 Runnable 接口来实现任务。

  • extends Thread,重写 run 方法
  • implements Runnable ,实现 run 方法

多线程的基本使用

Thread 和 Runnable

创建和启动

  • 创建 Thread 实例

我们首先要创建一个 Thread 实例,可以继承 Thread,或者传递一个 Runnable.

继承的方式

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("I'm new Thread");
    }
}

传递 Runnable 的方式, 可以使用 lambda 语法

     new Thread(()->{
            System.out.println("I'm new Thread");
        })
  • 调用 **start**方法

调用start()方法后,线程才会启动。虚拟机会创建一个线程来运行我们实现的run方法。

启动

new MyThread().start();

或者

new Thread(()->{
System.out.println("I'm new Thread");
}).start();

注意不可多次调用 start 方法。多次调用会抛出 IllegalThreadStateException。

Thread 类构建方法

Thread 实现了 Runnable 接口。

来看 Thread 类的构造方法,内部是调用 init 来初始化。

//片段1 init 方法
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals)

//片段2 构造函数
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

// 片段3 - 使用在init方法里初始化AccessControlContext类型的私有属性
this.inheritedAccessControlContext = 
    acc != null ? acc : AccessController.getContext();

// 片段4 - 两个对用于支持ThreadLocal的私有属性
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

init方法在片段一,见名知意,补充一下:

  • name:线程名,可重复,不指定名称,见片段2
  • acc: 初始化私有变量,见片段3

inheritedAccessControlContext 可以用来限制线程的执行权限。一般我们是不会使用它的,那什么时候会使用到这个变量呢?可以参考这个stackoverflow的问题:Restrict permissions to threads which execute third party softwareopen in new window

  • inheritThreadLocals:可继承的 ThreadLocal。见片段4.

实际情况下,我们大多直接调用下面两个构造方法

Thread(Runnable target)
Thread(Runnable target, String name)

Thread常用方法

静态方法

  • currentThread():返回当前执行线程对象的引用
  • yield(): 当前线程愿意放弃对当前处理器的占用。只是意愿,调用该方法后,有可能会继续运行当前线程。
  • sleep(): 当前线程睡眠一段时间

实例方法

  • join(): 当前线程等待 实例线程 die 后再执行

例如


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Thread Hello");
        });
        thread.start();
        thread.join();
        System.out.println("Main Hello");
    }

主线程会等 thread 线程执行完,再输出 "Main Hello"

Callable、Future、FutureTask

通常,我们使用 Runnable 和 Thread 来创建新的线程。但是有一个问题,就是 run 方法没有返回值。

JDK 提供了 CallableFuture 接口来解决这个问题,也就是所谓的异步模型。

Callable 接口

Callable 与 Runnable 类似,都是只有一个抽象方法的 lambda 接口。不同的是 Callable 接口有返回值,而且支持泛型。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Callable 的使用

Callable 一般配合线程池工具 ExecutorService 使用。

ExecutorService 提交一个 Callable ,返回一个 Future,结果可以通过 Future 的 get 方法得到。

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callable = () -> {
            return "hello,callable";
        };
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(callable);
        String s = future.get();
        System.out.println(s);
    }

输出:

hello,callable

Future 接口

Future 表示了异步计算的结果。

public interface Future<V> {
    boolean cancel(boolean paramBoolean);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long paramLong, TimeUnit paramTimeUnit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

常用方法有

  • get 获取异步结果
  • cancel 尝试取消线程执行,boolean参数表示是否采用中断方式取消线程。

所以,为了让任务有取消的功能,可以用 Callable 来代替 Runnable。

FutureTask 类

JDK为 Future 接口提供了实现,这个实现类叫做 FutureTask。

它实现了 Runnable 和 Future 接口。

创建 FutureTask 的接口。

public FutureTask(Callable<V> callable)

基本用法如下:

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask(() -> {
            return "hello,world";
        });
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(futureTask);
        String s = futureTask.get();
        System.out.println(s);
    }

为什么又FutureTask呢?因为 Future 只是一个接口,里面的 cancel 、 get 、 isDone 自己实现都非常复杂。所以JDK 提供了一个 FutureTask 供我们使用。

submit 提交的调用的是 submit(Runnable task)方法,是没有返回值的。结果要 通过 FutureTask 实例的 get方法来获取。

提交Callable是调用 submit(Callable<T> task),返回一个 Future,通过返回的Futrue来取值。

Last Updated:
Contributors: himcs