并发编程 线程创建——五种方法

继承Thread类

示例如下:

public static void main(String[] args) {
    System.out.println("主线程启动:"+Thread.currentThread().getName());
    new Thread02().start();
}

public class Thread02 extends Thread {
    @Override
    public void run() {
        System.out.println("子线程启动:"+Thread.currentThread().getName());
    }
}
  • 通过继承thread类实现的多线程 虽然实现的是run方法 但是要启动线程的话 应该是通过start方法 而你启动了多少次start就相当于启动了多少个子线程

    run方法相当于只是指定了子线程中需要处理的事情 而真正的创建、启动线程并不是run方法

    start方法也只是将线程调整到准备就绪的状态 cpu并不一定是立马执行 需要等待调度

  • run方法中的代码执行完毕后 线程就是死亡

  • 不推荐使用 避免OOP单继承局限性

实现Runnable接口

示例如下:

public static void main(String[] args) {
    // 通过lambda表达式的形式实现runnable接口 启动线程
    new Thread(()->{System.out.println("子线程启动:"+Thread.currentThread().getName());}).start();
}
  • 一般都是使用匿名内部类或者lambda表达式的形式实现runnable接口 实现新线程的启动 本质上thread类也是实现了runnable接口的 构造函数中也是可以接口runnable接口的 所以才支持这样的线程启动方式
  • 推荐使用 避免了单继承的局限性

使用Callable和FutureTask创建线程

示例如下:

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 实现任务对象
        FutureTask<String> task = new FutureTask<String>(() -> {
            System.out.println("子线程启动" + Thread.currentThread().getName());
            return "成功了";
        });
        // 将任务对象传递到新线程 启动
        new Thread(task).start();
        // 获取打印任务返回的结果
        System.out.println(task.get());
    }
  • 与runnable接口不同 callable接口可以实现有返回值的线程创建 并且可以抛出异常
  • 实现了callable接口任务 需要装载futuretask对象中 因为futuretask对象继承了runnable接口 所以可以通过futuretask对象来启动线程 多线程任务的返回结果可以通过futuretask获取
  • 通过get获取线程任务的返回值 需要注意的是 get可能会造成阻塞 因为在get的时候是在等待线程任务完成 如果在get之前线程任务完成了 那么就不会造成阻塞

使用线程池创建线程

示例如下:

public static void main(String[] args)  {
    // region thread pool 实现方式
    // 使用executors创建一个线程池
    ExecutorService executorService = Executors.newCachedThreadPool();
    
    // 线程池实现runnable接口启动线程
    executorService.execute(() -> {
        System.out.println("子线程启动:" + Thread.currentThread().getName());
    });

    // 线程池实现callable接口启动线程
    Future<?> submit = executorService.submit(() -> {
        System.out.println("子线程启动:" + Thread.currentThread().getName());
        return "成功了";
    });
    // 还是通过futuretask获取返回值
    System.out.println(submit.get());
    // endregion
}
  • 线程池的话 不用手动创建thread类 而是通过线程池的Executors框架来创建线程开启线程
  • 使用Executors对象创建线程池 返回一个线程服务对象 通过线程服务对象 来操作和管理线程
  • 线程池种类:
    • newCachedThreadPool 创建一个可缓存线程池
      如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    • newFixedThreadPool 创建一个定长线程池
      可控制线程最大并发数 超出的线程会在队列中等待
    • newScheduledThreadPool 创建一个定长线程池
      支持定时及周期性任务执行
    • newSingleThreadExecutor 创建一个单线程化的线程池
      它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

spring @Async异步注解

示例如下:

@Async
public void springThread() throws InterruptedException {
    Thread.sleep(3000);
    System.out.println("子线程启动:"+Thread.currentThread().getName());
}
  • 标记了@Async的方法在spring中将被认作异步方法 在调用的时候会开启新线程来处理
  • spring的@async注解底层实现应该使用了线程池 这里不做过深的研究
posted @ 2021-08-12 10:03  熏晴微穗  阅读(50)  评论(0编辑  收藏  举报