Java 线程创建的几种方式

简介: 我们在工作中经常会用到多线程开发,今天我们一起来梳理一下线程创建的几种方式:继承 Thread 线程类实现 Runnable 接口创建 Callable 接口的实现类使用拉姆达表达式创建(1.8)ForkJoinPool 线程池继承 RecursiveAction, 无返回值继承 RecursiveTask, 带返回值的使用线程池 ThreadPoolExecutor使用并发包 Executors 工具类创建

继承 Thread 类


继承线程类,然后重写 run 方法,调用线程 start 方法后, 等待  JVM 为当前线程分配到 CPU 时间片会调用 run 方法执行。


class Thread1 extends Thread {
    @Override  
    public void run() {
        //dosomething()
        System.out.println("Thread1");
    }
}


实现 Runnable 接口


实现 Runnable 接口,重写 run 方法,执行线程需要丢入 Runnable 接口实现类,调用 Thread 对象的 start 方法执行。


// 定义线程
class Runner1 implements Runnable {
    @Override  
    public void run() {
        //dosomething()
        System.out.println("Runner1");
    }
}
// 线程执行
new Thread thread = new Thread(new Runner1());
thrad.start();


创建 Callable 接口的实现类


Callable: 返回结果并且可能抛出异常的任务。 优点:


  • 可以获得任务执行返回值;


  • 通过与 Future 的结合,可以实现利用 Future 来跟踪异步计算的结果。


class Callable1 implements Callable {  
  @Override
  public String call() throws Exception {
    return "1";
  }
}
FutureTask stringFutureTask = new FutureTask<>(new Callable1());
stringFutureTask.run();


使用流创建


其实本质是通过 Lambda(拉姆达)表示创建线程,也就是得到一个 Runnable 接口的实例。


Runnable runnable = () -> {
  System.out.println("createThreadForStream");
};
new Thread(runnable).start();


ForkJoinPool 线程池


ForkJoinPool:Java提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合成总的计算结果。ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。ForkJoinPool提供了如下两个常用的构造器。


public ForkJoinPool(int parallelism):创建一个包含parallelism个并行线程的ForkJoinPool。public ForkJoinPool():以 Runtime.getRuntime().availableProcessors()的返回值作为 parallelism 来创建 ForkJoinPool。


创建ForkJoinPool实例后,可以调用 ForkJoinPool 的 submit(ForkJoinTask task) 或者invoke(ForkJoinTask task) 来执行指定任务。其中 ForkJoinTask 代表一个可以并行、合并的任务。ForkJoinTask 是一个抽象类,它有两个抽象子类:RecursiveAction 和 RecursiveTask。


  • RecursiveTask:代表有返回值的任务;


  • RecursiveAction:代表没有返回值的任务;


我们就可以通过 RecursiveTask、 RecursiveAction 实例来创建线程。


new ForkJoinPool().submit(new Thread1());


继承 RecursiveAction 无返回值


继承 RecursiveAction 我们可以创建不带返回值的线程对象。


class PrintTask extends RecursiveAction {  
   @Override
    protected void compute() {
        System.out.println("RecursiveAction 子类");
    }
}
new ForkJoinPool().submit(new PrintTask());


继承 RecursiveTask 获取返回值


继承 RecursiveTask我们可以创建带返回值的线程。


class CalcuteTask extends RecursiveTask {
  @Override
  protected Integer compute() {
    return 10;
  }
}
Executors.newSingleThreadExecutor().submit(new CalcuteTask());
ForkJoinTask submit = new ForkJoinPool().submit(new CalcuteTask());
Integer res = submit.get();


使用线程池


ThreadPoolExecutor是我们创建线程池的核心工具类,我们创建线程池后。execute或者 submit方法提交线程执行任务,进入线程池中。等待线程池为我们分配资源执行。并且 ThreadPoolExecutor 提供了非常丰富的线程池配置参数。


通过 ThreadPoolExecutor 创建线程池,也是 Alibaba java 编程规范中倡导的变成方式。


ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
threadPoolExecutor.execute(new Thread1());


使用并发包 Executors


Executors其实就是对 ThreadPoolExecutor的封装,是一个典型的工厂方法模式。提供了非常多的创建线程池的方法。比如:


  • public static ExecutorService newFiexedThreadPool(int Threads) 创建固定数目线程的线程池。


  • public static ExecutorService newCachedThreadPool():创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。


  • public static ExecutorService newSingleThreadExecutor():创建一个单线程的Executor。


  • public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。


Executors.newSingleThreadExecutor().execute(new Thread1());
Executors.newFixedThreadPool(8).execute(new Thread1());
Executors.newWorkStealingPool(8).execute(new Thread1());
Executors.newCachedThreadPool().execute(new Thread1());
Executors.newSingleThreadScheduledExecutor().schedule(new Thread1(), 2, TimeUnit.SECONDS);
Executors.newScheduledThreadPool(8).schedule(new Thread1(), 2, TimeUnit.SECONDS);


总结


Java 创建线程任务过后,JVM 底层会创建一个 JavaThread 和一个 OSThread, 将 Java 线程对象和操作系统内核态的进程绑定。


相关文章
|
4天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
42 17
|
15天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
17天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
17天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
17天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
42 3
|
17天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
101 2
|
25天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
48 6
|
2月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####