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

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方法的时候再将这个后果返回。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理