Skip to content

自我介绍

技术考查点:个人基本情况、职业素养、沟通能力、对自身优势的总结与表达能力。 回答:您好,我叫[姓名],毕业于[毕业院校]的[专业]。我拥有[X]年的Java开发经验,曾在[公司名称]担任Java开发工程师。在工作中,我主要负责[具体项目名称]的开发与维护,熟悉Spring、Spring Boot、MyBatis等框架,具备良好的代码编写习惯和问题解决能力。我对新技术有强烈的学习兴趣,能够快速掌握并应用到实际项目中。我相信自己的专业技能和工作经验能够为贵公司的项目做出贡献。 流程图:无 代码示例:无

类的生命周期

技术考查点:对Java类加载机制的理解,包括类的加载、连接、初始化、使用和卸载的整个过程。 回答:Java类的生命周期包括以下几个阶段:

  1. 加载:通过类加载器将类的字节码文件加载到内存中,并在方法区创建一个Class对象。
  2. 连接:又分为验证、准备和解析三个子阶段。验证确保字节码文件的正确性;准备为类的静态变量分配内存并设置初始值;解析将符号引用转换为直接引用。
  3. 初始化:执行类的静态代码块和静态变量的赋值操作。
  4. 使用:创建类的实例,调用类的方法等。
  5. 卸载:当类的Class对象不再被引用,且垃圾回收器认为可以回收时,将类的字节码从内存中卸载。 流程图:无 代码示例:无

生命周期中的验证是验证什么

技术考查点:对Java类加载过程中验证阶段的深入理解,了解验证的目的和内容。 回答:在类的生命周期的验证阶段,主要验证以下几个方面:

  1. 文件格式验证:验证字节码文件是否符合Java虚拟机规范,例如魔数是否正确、版本号是否支持等。
  2. 元数据验证:对类的元数据进行语义分析,确保类的定义符合Java语言规范,例如是否存在父类、是否实现了接口等。
  3. 字节码验证:对类的方法体进行语义分析,确保方法体中的字节码指令合法、安全,例如类型转换是否合法、操作数栈和局部变量表的使用是否正确等。
  4. 符号引用验证:在解析阶段,验证符号引用所引用的类、字段、方法等是否存在,以及访问权限是否合法。 流程图:无 代码示例:无

jvm的内存结构

技术考查点:对Java虚拟机内存结构的了解,包括各个区域的作用和特点。 回答:JVM的内存结构主要包括以下几个部分:

  1. 程序计数器:记录当前线程执行的字节码指令的地址,是线程私有的。
  2. Java虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,是线程私有的。
  3. 本地方法栈:与虚拟机栈类似,用于支持本地方法的调用,是线程私有的。
  4. Java堆:是JVM中最大的一块内存区域,用于存储对象实例,是所有线程共享的。
  5. 方法区:用于存储类的元数据、常量池、静态变量等信息,是所有线程共享的。 流程图:无 代码示例:无

程序计数器的功能

技术考查点:对程序计数器在JVM中的作用的理解。 回答:程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。 流程图:无 代码示例:无

八大数据类型是什么对应的包装类型是什么

技术考查点:对Java基本数据类型和其对应的包装类的掌握。 回答:Java的八大数据类型及其对应的包装类型如下:

基本数据类型包装类型
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
流程图:无
代码示例
java
// 基本数据类型转包装类型
int num = 10;
Integer integerNum = Integer.valueOf(num);

// 包装类型转基本数据类型
Integer integer = 20;
int basicNum = integer.intValue();

Java异常的分类

技术考查点:对Java异常体系的了解,包括异常的分类和层次结构。 回答:Java异常分为两大类:Error和Exception。

  1. Error:表示系统级的错误和程序无法处理的异常,通常是由虚拟机或底层系统抛出的,例如OutOfMemoryError、StackOverflowError等,程序通常无法捕获和处理这些异常。
  2. Exception:表示程序可以捕获和处理的异常,又分为受检查异常(Checked Exception)和运行时异常(Runtime Exception)。受检查异常在编译时必须进行处理,否则会导致编译错误,例如IOException、SQLException等;运行时异常在编译时不需要进行处理,通常是由程序逻辑错误引起的,例如NullPointerException、ArrayIndexOutOfBoundsException等。 流程图:无 代码示例:无

编译时期异常和运行时期异常的区别

技术考查点:对编译时期异常和运行时期异常的特点和区别的理解。 回答:编译时期异常和运行时期异常的主要区别如下:

  1. 定义:编译时期异常是指在编译阶段必须进行处理的异常,否则程序无法通过编译;运行时期异常是指在运行阶段可能出现的异常,编译阶段不需要进行处理。
  2. 继承关系:编译时期异常通常继承自Exception类,但不包括RuntimeException及其子类;运行时期异常继承自RuntimeException类。
  3. 处理方式:编译时期异常必须使用try-catch语句进行捕获处理,或者使用throws关键字将异常抛出;运行时期异常可以不进行处理,由JVM自动捕获和处理。
  4. 产生原因:编译时期异常通常是由外部因素引起的,例如文件不存在、网络连接失败等;运行时期异常通常是由程序逻辑错误引起的,例如空指针引用、数组越界等。 流程图:无 代码示例
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionExample {
    public static void main(String[] args) {
        // 编译时期异常
        try {
            FileInputStream fis = new FileInputStream("test.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // 运行时期异常
        int[] arr = new int[5];
        System.out.println(arr[10]); // 会抛出ArrayIndexOutOfBoundsException
    }
}

描述常用的集合框架

技术考查点:对Java集合框架的了解,包括常用集合类的特点和使用场景。 回答:Java集合框架主要分为两大体系:Collection和Map。

  1. Collection体系
    • List:有序、可重复的集合,常用的实现类有ArrayList、LinkedList和Vector。ArrayList基于数组实现,适合随机访问;LinkedList基于链表实现,适合插入和删除操作;Vector是线程安全的,但性能较低。
    • Set:无序、不可重复的集合,常用的实现类有HashSet、TreeSet和LinkedHashSet。HashSet基于哈希表实现,不保证元素的顺序;TreeSet基于红黑树实现,元素会按照自然顺序或指定的比较器顺序排序;LinkedHashSet基于哈希表和链表实现,保证元素的插入顺序。
    • Queue:队列,遵循先进先出(FIFO)的原则,常用的实现类有LinkedList和PriorityQueue。LinkedList可以作为队列使用,PriorityQueue是一个优先队列,元素会按照优先级进行排序。
  2. Map体系:键值对的集合,常用的实现类有HashMap、TreeMap和LinkedHashMap。HashMap基于哈希表实现,不保证键的顺序;TreeMap基于红黑树实现,键会按照自然顺序或指定的比较器顺序排序;LinkedHashMap基于哈希表和链表实现,保证键的插入顺序。 流程图:无 代码示例
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class CollectionExample {
    public static void main(String[] args) {
        // List示例
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        System.out.println(list);

        // Set示例
        Set<String> set = new HashSet<>();
        set.add("apple");
        set.add("banana");
        System.out.println(set);

        // Map示例
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        System.out.println(map);
    }
}

arraylist可以存空值吗

技术考查点:对ArrayList特性的了解。 回答:ArrayList可以存储空值。ArrayList是一个动态数组,它允许存储任意数量的元素,包括null值。当向ArrayList中添加null值时,ArrayList会将其作为一个普通的元素进行存储。 流程图:无 代码示例

java
import java.util.ArrayList;
import java.util.List;

public class ArrayListNullExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add(null);
        list.add("apple");
        System.out.println(list);
    }
}

字节流和字符流读取什么文件

技术考查点:对字节流和字符流的适用场景的理解。 回答:字节流和字符流都可以用于读取文件,但它们的适用场景有所不同。

  1. 字节流:字节流以字节为单位进行读写操作,适用于处理二进制文件,例如图片、音频、视频等。常见的字节流类有FileInputStream、FileOutputStream、BufferedInputStream和BufferedOutputStream等。
  2. 字符流:字符流以字符为单位进行读写操作,适用于处理文本文件,例如Java源文件、HTML文件、TXT文件等。字符流在读写过程中会进行字符编码和解码,能够更好地处理字符数据。常见的字符流类有FileReader、FileWriter、BufferedReader和BufferedWriter等。 流程图:无 代码示例
java
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;

public class StreamExample {
    public static void main(String[] args) {
        // 字节流读取文件
        try (InputStream is = new FileInputStream("test.txt")) {
            int data;
            while ((data = is.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 字符流读取文件
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

线程的状态

技术考查点:对Java线程状态的了解,包括线程状态的转换和控制。 回答:Java线程有以下几种状态:

  1. 新建(New):线程对象被创建,但还没有调用start()方法。
  2. 就绪(Runnable):线程调用了start()方法,进入就绪状态,等待CPU调度。
  3. 运行(Running):线程获得CPU时间片,正在执行。
  4. 阻塞(Blocked):线程因为某些原因放弃CPU时间片,暂时停止执行,例如等待锁、等待I/O操作等。
  5. 等待(Waiting):线程调用了Object.wait()、Thread.join()或LockSupport.park()等方法,进入等待状态,需要其他线程唤醒。
  6. 超时等待(Timed Waiting):线程调用了Thread.sleep()、Object.wait(long)、Thread.join(long)或LockSupport.parkNanos()等方法,进入超时等待状态,在指定的时间后会自动唤醒。
  7. 终止(Terminated):线程执行完毕或因为异常退出。 流程图:无 代码示例:无

创建线程的方法

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}

项目的技术亮点

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}

ioc的逻辑,由谁来管理

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}

aop实现方式

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}

springcloud用过哪些组件

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}

gateway和nginx的区别

技术考查点:对Java创建线程的方式的掌握。 回答:在Java中,创建线程有以下几种常见的方法:

  1. 继承Thread类:创建一个类继承自Thread类,并重写run()方法,然后创建该类的实例并调用start()方法启动线程。
  2. 实现Runnable接口:创建一个类实现Runnable接口,并重写run()方法,然后将该类的实例作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。
  3. 实现Callable接口:创建一个类实现Callable接口,并重写call()方法,该方法可以有返回值。然后使用FutureTask类包装Callable对象,再将FutureTask对象作为参数传递给Thread类的构造函数,最后调用start()方法启动线程。可以通过FutureTask的get()方法获取线程的返回值。
  4. 使用线程池:使用ExecutorService接口和Executors工具类创建线程池,将实现Runnable或Callable接口的任务提交给线程池执行。 流程图:无 代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

// 继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类的线程正在运行");
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口的线程正在运行");
    }
}

// 实现Callable接口
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "实现Callable接口的线程返回结果";
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 继承Thread类创建线程
        MyThread thread1 = new MyThread();
        thread1.start();

        // 实现Runnable接口创建线程
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();

        // 实现Callable接口创建线程
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        System.out.println(futureTask.get());

        // 使用线程池创建线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);
    }
}