java开启线程的几种方法

Springboot启动过程详解_springboot启动流程-优快云博客

这个之后在看

JAVA开启线程的四种方法-优快云博客

预备知识:

为什么需要单继承?

  • 避免复杂性:如果允许一个类从多个类继承,可能会引发“菱形继承问题”或其他复杂性。例如,两个父类可能有同名的方法,这时子类会不知道该继承哪个版本,增加了设计的复杂度。
  • 清晰性:单继承确保了类结构简单、层次清晰,减少了冲突和混淆。

多态和接口的作用

虽然在 单继承 机制中,一个类只能继承一个父类,但 Java 提供了 接口interface),允许类实现多个接口,这在某种程度上弥补了单继承的限制,提供了更大的灵活性。

例如,一个类可以继承一个父类,并实现多个接口

1,继承Thread类

继承Thread类,重写run方法(不推荐,因为java的单继承局限性)

2,实现Runnable接口

方式一:直接实现Runnable接口(避免单继承的局限性,方便共享资源,推荐使用)

方式二:匿名内部类

匿名内部类本质上也是一个类实现了Runnable接口,重写了Thread类的run方法,只不过这个类没有名字(所以是匿名),直接作为参数传入Thread类

public class Anonymous {
 
    /*
    * 创建步骤如下:
    * 匿名内部类本质上也是一个类实现了Runnable接口,重写了run方法,只不过这个类没有名字,直接作为参数传入Thread类
    *
    * 调用示例:
    * //开启10个线程
    * for (int i = 0; i < 10; i++) {
    *     Anonymous anonymous =new Anonymous();
    *     anonymous.myRun();
    * }
    *
    * */
    public void myRun(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                doSomething();
            }
        }).start();
    }
 
    /**
     * 需要处理的任务
     * */
    private void doSomething(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "执行" + i);
        }
    }
}

3,实现Callable接口
     * 创建步骤如下:
     * 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
     * 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
     * 3,将FutureTask的实例,作为Thread的target来创建Thread类
     * 4,调用start方法,开启线程
     *
     * 调用示例:
     * Callable<String> tc = new CallableImpl();
     * FutureTask<String> task = new FutureTask<>(tc);
     * new Thread(task).start();
     * try {
     *     System.out.println(task.get());
     * } catch (InterruptedException | ExecutionException e) {
     *     e.printStackTrace();
     * }
     *

public class CallableImpl implements Callable<String> {
 
    /*
     * 创建步骤如下:
     * 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
     * 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
     * 3,将FutureTask的实例,作为Thread的target来创建Thread类
     * 4,调用start方法,开启线程
     *
     * 调用示例:
     * Callable<String> tc = new CallableImpl();
     * FutureTask<String> task = new FutureTask<>(tc);
     * new Thread(task).start();
     * try {
     *     System.out.println(task.get());
     * } catch (InterruptedException | ExecutionException e) {
     *     e.printStackTrace();
     * }
     *
     * 说明:
     * 1.与使用Runnable相比, Callable功能更强大些
     * 2.实现的call()方法相比run()方法,可以返回值
     * 3.方法可以抛出异常
     * 4.支持泛型的返回值
     * 5.需要借助FutureTask类,比如获取返回结果
     * Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
     * FutureTask是Futrue接口的唯一的实现类
     * FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
     *
     * */
    private int ticket = 5;
 
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(doSomething());
        }
 
        return "出票任务完成";
    }
 
    public String doSomething() {
        String result = "";
        if (this.ticket > 0) {
            result = "出票成功,ticket=" + this.ticket--;
        } else {
            result = "出票失败,ticket=" + this.ticket;
        }
        return result;
    }
}
1. Callable 接口:

Callable 是 Java 中一个更强大的接口,相比于 Runnable,它的主要特点是:

  • 可以返回结果Callable 接口中的 call() 方法可以返回一个结果,而 Runnable 中的 run() 方法是 void,不能返回值。
  • 可以抛出异常Callablecall() 方法可以抛出异常,而 Runnablerun() 方法不允许抛出异常。
  • 支持泛型Callable<V> 可以返回指定类型 V 的结果。

在代码中,CallableImpl 实现了 Callable<String>,因此 call() 方法返回一个 String 类型的结果。

2. FutureTask
  • FutureTask 是 Java 中用于包装 CallableRunnable 的类,它既可以作为一个 Runnable 被线程执行,也可以通过它的 get() 方法来获取 Callable 返回的结果。
  • FutureTask 实现了 Future 接口,可以用来查询任务的状态、取消任务或者获取任务的执行结果。
3. 任务逻辑:
  • CallableImpl 类模拟了一个简单的出票逻辑。call() 方法会循环 10 次调用 doSomething() 方法,每次根据剩余票数判断出票是否成功,并返回一个字符串。
  • 任务完成后,call() 方法返回一个字符串结果 "出票任务完成"

调用示例:

  • 第1步Callable<String> tc = new CallableImpl(); 创建一个 CallableImpl 实例。(任务内容)
  • 第2步FutureTask<String> task = new FutureTask<>(tc); 使用 FutureTaskCallable 包装起来。FutureTask 可以同时作为一个 Runnable 被线程执行,并且能够返回任务结果。(任务里面放内容tc)
  • 第3步new Thread(task).start(); 创建并启动一个线程,线程的任务是执行 Callable 中的 call() 方法。
  • 第4步task.get(); 通过 FutureTaskget() 方法获取任务执行的结果。如果任务没有完成,它会阻塞直到结果可用。

-------------------------------------------------------------------------------

4,创建线程池********

Executors工具类实现(很方便)

Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的

 /*
     * 创建步骤如下:
     * 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
     * 2,创建一个自定义线程个数的线程池
     * 3,实例化Runnable接口的实现类
     * 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
     * 5,关闭线程池
     *
     * 调用示例:
     * ExecutorService pool = Executors.newFixedThreadPool(2);
     * ThreadPool threadPool = new ThreadPool("AA");
     * ThreadPool threadPoo2 = new ThreadPool("BB");
     * pool.execute(threadPool);
     * pool.execute(threadPoo2);
     * pool.shutdown();
     *
     * 说明:
     * 示例中创建的是2个线程的线程池
     * execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程

public class ThreadPool implements Runnable {
 
    /*
     * 创建步骤如下:
     * 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
     * 2,创建一个自定义线程个数的线程池
     * 3,实例化Runnable接口的实现类
     * 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
     * 5,关闭线程池
     *
     * 调用示例:
     * ExecutorService pool = Executors.newFixedThreadPool(2);
     * ThreadPool threadPool = new ThreadPool("AA");
     * ThreadPool threadPoo2 = new ThreadPool("BB");
     * pool.execute(threadPool);
     * pool.execute(threadPoo2);
     * pool.shutdown();
     *
     * 说明:
     * 示例中创建的是2个线程的线程池
     * execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程
     *
     * */
    String name;
    public ThreadPool(String name) {
        this.name = name;
    }
 
    @Override
    public void run() {
        doSomething();
    }
 
    /**
     * 需要处理的任务
     * */
    private void doSomething() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "执行" + i + ",name=" + this.name);
        }
    }
}

示例流程:

  1. 定义 Runnable 接口的实现类

    • ThreadPool 类实现了 Runnable 接口,因此可以用来创建并发任务。
  2. 创建线程池

    • 使用 Executors.newFixedThreadPool(2) 创建了一个具有固定大小为 2 的线程池,这意味着线程池中最多同时运行两个线程。
  3. 实例化 ThreadPool 类(实现了 Runnable 接口)

    • ThreadPool threadPool = new ThreadPool("AA"); 实例化了一个 ThreadPool 对象,并传递了名字 "AA"。
    • ThreadPool threadPoo2 = new ThreadPool("BB"); 同样创建了另一个名为 "BB" 的 ThreadPool 实例。
  4. 将(实例化对象)任务提交给线程池执行

    • pool.execute(threadPool); 将第一个 ThreadPool 实例提交给线程池,线程池将分配一个线程来执行该任务。
    • pool.execute(threadPoo2); 提交第二个任务,线程池将分配另一个线程来并行执行这个任务。
  5. 关闭线程池

    • pool.shutdown(); 在所有提交的任务执行完毕后关闭线程池,不再接收新的任务。
doSomething() 方法:
  • 这是 ThreadPool 类中的实际任务,模拟执行了一个简单的循环任务。每次循环都会输出当前线程的名称以及任务的执行进度。

ThreadPoolExecutor执行Runnable任务(以下见下面那个链接)

ThreadPoolExecutor执行Callable任务(和runable区别就是都要用feature,比runable复杂一丢丢)

Java六种常用线程创建执行方法_java 线程执行-优快云博客

FutureTaskFuture 在 Java 中都用于并发编程,尤其是在执行异步任务时获取任务的执行结果。它们有一些相似之处,但也有明显的区别。

1. Future 接口

Future 是一个接口,它的主要功能是表示异步计算的结果。它提供了一些方法来检查任务的状态、获取结果、取消任务等。

关键方法:
  • get(): 获取任务的执行结果。如果任务没有完成,这个方法会阻塞,直到结果可用。
  • cancel(boolean mayInterruptIfRunning): 尝试取消任务。
  • isDone(): 检查任务是否已经完成。
  • isCancelled(): 检查任务是否已经被取消。
适用场景:

Future 主要用于提交给 线程池 的任务,线程池会返回一个 Future 对象,用户可以使用这个 Future 来获取任务执行的状态和结果。

示例:
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "任务完成";
});

try {
    // 阻塞等待任务执行完成并获取结果
    String result = future.get();
    System.out.println(result);  // 输出 "任务完成"
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

2. FutureTask 类

FutureTaskFuture 接口的一个具体实现类,除了具备 Future 的所有功能之外,它还实现了 Runnable 接口,意味着它可以作为一个 任务 被线程执行。这使得它既是一个 Runnable,可以在线程中执行任务,又是一个 Future,可以获取任务的执行结果。

特性:
  • FutureTask 可以包装一个 CallableRunnable,然后通过 Thread 或者 Executor 来执行。
  • 它既可以用来获取任务执行的结果,又可以通过 Runnable 来被线程直接运行。
适用场景:

FutureTask 适用于需要创建一个可以返回结果的任务,并手动控制任务的执行,而不仅仅依赖线程池。

示例:
Callable<String> callable = () -> {
    Thread.sleep(1000);
    return "任务完成";
};

// 用 FutureTask 包装 Callable
FutureTask<String> futureTask = new FutureTask<>(callable);

// 将 FutureTask 作为 Runnable 放入线程中执行
new Thread(futureTask).start();

try {
    // 阻塞等待任务执行完成并获取结果
    String result = futureTask.get();
    System.out.println(result);  // 输出 "任务完成"
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

区别:

  1. 功能上

    • Future 是一个接口,表示异步计算的结果,可以用来监控任务状态、取消任务或获取任务结果。
    • FutureTaskFuture 的实现类,同时实现了 Runnable 接口,因此它既可以当作任务执行,也可以用来获取任务结果。
  2. 使用方式

    • Future 通常是通过 线程池(如 ExecutorService)提交任务后返回的结果。
    • FutureTask 可以独立使用,既可以提交给线程池执行,也可以直接通过 Thread 来执行。
  3. 任务的执行

    • Future 本身不能执行任务,它只是一个结果的占位符。
    • FutureTask 本身是 Runnable,可以直接被线程执行。

总结:

  • Future 是一个接口,主要用于获取异步任务的结果和状态。
  • FutureTask 是一个具体的实现,既能表示异步任务的结果,还能作为一个可执行的任务被线程执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值