关于前端框架:大前端2022版全面升级完结无密内置文档资料

2次阅读

共计 2391 个字符,预计需要花费 6 分钟才能阅读完成。

download:大前端 2022 版全面降级完结无密内置文档资料

FutureTask 源码深度剖析
在 JDK 的 FutureTask 当中会使用到一个工具 LockSupport,在正式介绍 FutureTask 之前咱们先熟悉一下这个工具。
LockSupport 次要是用于阻塞和唤醒线程的,它次要是通过包装 UnSafe 类,通过 UnSafe 类当中的方法进行实现的,他底层的方法是通过依赖 JVM 实现的。在 LockSupport 当中次要有以下三个方法:

unpark(Thread thread))方法,这个方法可能给线程 thread 发放一个许可证,你可能通过多次调用这个方法给线程发放许可证,每次调用都会给线程发放一个许可证,然而这个许可证不能够进行累计,也就是说一个线程能够具备的最大的许可证的个数是 1 一个。

park()方法,这个线程会生产调用这个方法的线程一个许可证,因为线程的默认许可证的个数是 0,如果调用一次那么许可证的数目就变成 -1,当许可证的数目小于 0 的时候线程就会阻塞,因此如果线程从来没用调用 unpark 方法的话,那么在调用这个方法的时候会阻塞,如果线程在调用 park 方法之前,有线程调用 unpark(thread)方法,给这个线程发放一个许可证的话,那么调用 park 方法就不会阻塞。

parkNanos(long nanos)方法,同 park 方法一样,nanos 示意最长阻塞超时工夫,超时后 park 方法将主动返回,如果调用这个方法的线程有许可证的话也不会阻塞。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class Demo {

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(() -> {LockSupport.park(); // 没有许可证 阻塞住这个线程
  try {TimeUnit.SECONDS.sleep(1);
  } catch (InterruptedException e) {e.printStackTrace();
  }
  System.out.println("阻塞实现");
});
thread.start();
TimeUnit.SECONDS.sleep(2);
LockSupport.unpark(thread); // 给线程 thread 发放一个许可证
System.out.println("线程启动");

}
}

复制代码
下面代码的执行后果
线程启动
阻塞实现
复制代码
从下面代码咱们可能知道 LockSupport.park()可能阻塞一个线程,因为如果没有阻塞的话必定会先打印阻塞实现,因为打印这句话的线程只休眠一秒,主线程休眠两秒。
在源代码当中你可能会遇到 UNSAFE.compareAndSwapXXX 的代码,这行代码次要是进行原子交换操作 CAS,比如:
UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)))
复制代码
下面的代码次要是将 this 对象当中的内存偏移地址为 stateOffset 的对象拿进去与 NEW 进行比较,如果等于 NEW 那就将这个值设置为 CANCELLED,这整个操作是原子的(因为可能多个线程同时调用这个函数,因此需要保障操作是原子的),如果操作胜利返回 true 反之返回 false。如果你目前不是很理解也没关系,只需要知道它是将对象 this 的内存偏移为 stateOffset 的值替换为 CANCELLED 就行,如果这个操作胜利返回 true,不胜利返回 false。

FutureTask 回顾
咱们首先来回顾一下 FutureTask 的编程步骤:

写一个类实现 Callable 接口。

@FunctionalInterface
public interface Callable<V> {

/**
 * Computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws Exception if unable to compute a result
 */
V call() throws Exception;

}
复制代码
实现接口就实现 call 即可,可能看到这个函数是有返回值的,而 FutureTask 返回给咱们的值就是这个函数的返回值。

new 一个 FutureTask 对象,并且 new 一个第一步写的类,new FutureTask<>(callable 实现类)。
最初将刚刚失去的 FutureTask 对象传入 Thread 类当中,而后启动线程即可 new Thread(futureTask).start();。
而后咱们可能调用 FutureTask 的 get 方法失去返回的后果 futureTask.get();。

可能你会对 FutureTask 的使用形式感觉困惑,或者不是很明显,现在咱们来认真捋一下思路。

首先启动一个线程要么是继承自 Thread 类,而后重写 Thread 类的 run 方法,要么是给 Thread 类传送一个实现了 Runnable 的类对象,当然可能用匿名外部类实现。
既然咱们的 FutureTask 对象可能传送给 Thread 类,说明 FutureTask 必定是实现了 Runnable 接口,事实上也的确如此

可能发现的是 FutureTask 确实实现了 Runnable 接口,同时还实现了 Future 接口,这个 Future 接口次要提供了前面咱们使用 FutureTask 的一系列函数比如 get。

看到这里你应该能够大抵想到在 FutureTask 中的 run 方法会调用 Callable 当中实现的 call 方法,而后将后果保存下来,当调用 get 方法的时候再将这个后果返回。

正文完
 0