Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)

简介: 这一系列的文章暂不涉及Java多线程开发中的底层原理以及JMM、JVM部分的解析(将另文总结),主要关注实际编码中Java并发编程的核心知识点和应知应会部分。说在前面,Java并发编程的实质,是线程对象调用start方法启动多线程,而线程对象则必须是Thread类或其子类实现。

这一系列的文章暂不涉及Java多线程开发中的底层原理以及JMM、JVM部分的解析(将另文总结),主要关注实际编码中Java并发编程的核心知识点和应知应会部分。

说在前面,Java并发编程的实质,是线程对象调用start方法启动多线程,而线程对象则必须是Thread类或其子类实现。Runnable和Callable的作用类似于Comparable、Serializable,是用于被并发的类实现的接口,从而使得Thread类可以在初始化时传入这个被并发的类。此是大前提。本文从多线程实现和启动出发,对这些类或接口予以说明。

Thread

通过Thread的子类创建多线程的步骤如下:

1. 创建Thread的子类,并重写run()方法,该方法即为线程执行体。

2. 创建Thread子类的对象,即为线程对象。

3. 调用线程对象的start()方法启动线程。

 1 public class TestThread extends Thread{
 2     
 3     public TestThread(String name) {
 4         setName(name);
 5     }    
 6     @Override
 7     public void run() {
 8         while(!interrupted())
 9             System.out.println(getName() + "线程执行了");
10     }    
11     public static void main(String[] args) {
12         
13         TestThread t1 = new TestThread("first");
14         TestThread t2 = new TestThread("second");
15         //setDaemon()设置线程为守护线程
16 //        t1.setDaemon(true);
17 //        t2.setDaemon(true);        
18         t1.start();
19         t2.start();        
20         t1.interrupt();
21     }
22 }

Runnable

需要并发执行的类,可以通过实现Runnable接口,作为Thread的Target来创建线程对象。

 1 public class TestRunnable implements Runnable{
 2 
 3     @Override
 4     public void run() {
 5         while(true) {
 6             System.out.println("thread running...");
 7             try {
 8                 Thread.sleep(1000);
 9             } catch (InterruptedException e) {
10                 e.printStackTrace();
11             }
12         }
13     }
14     
15     public static void main(String[] args) {
16         //传入TestRunnable对象作为Target, 开启线程
17         Thread t = new Thread(new TestRunnable());
18         t.start();
19         //采用匿名内部类的方式创建和启动线程
20         new Thread() {
21             @Override
22             public void run() {
23                 System.out.println("Thread的匿名内部类");
24             }
25         }.start();
26         //父类采用匿名实现Runnable接口, 并由子类继承
27         new Thread(new Runnable() {
28             
29             @Override
30             public void run() {
31                 System.out.println("父类的线程");
32             }
33         }) {
34             @Override
35             public void run() {
36                 System.out.println("子类的线程");
37             }
38         }.start();        
39     }
40 }

Callable和Future

Java5开始提供了Callable接口,用于现有多线程开发的强力补充。Callable接口提供一个call()方法来构造线程执行体。

1. call()方法可以有返回值

2. call()方法可以声明抛出异常

因此Callable接口没有继承Runnable接口,不能直接作为Thread类的Target来构造线程对象,所以Java5提供了Future接口来代表call方法的返回值。

Future提供了FutureTask实现类,该实现类实现了Future接口和Runnable接口,像桥梁一样把线程执行体和线程对象连接了起来。

Future接口提供了若干公共方法来操作Callable任务:

  • boolean cancel(boolean mayInterruptIfRunning): 试图取消Future里关联的Callable任务
  • V get():返回Callable任务里call方法的返回值。调用该方法会导致阻塞,必须等子线程完成后才得到返回值
  • V get(long timeout, TimeUnit unit):最多阻塞timeout和unit指定的时间,超时将抛出TimeoutException异常
  • boolean isCancelled():Callable任务正常完成前被取消,则返回true
  • boolean isDone():Callable任务已完成,则返回true

创建并启动有返回值的线程步骤如下:

1. 创建Callable接口的实现类,并实现call方法作为线程执行体,再创建类的实例。Java8中可通过Lambda表达式进行。

2. 使用FutureTask类来包装Callable实现类的对象

3. 使用FutureTask作为Thread对象的target

4. 使用FutureTask对象的get方法获取子线程执行后的返回值

Callable接口和FutureTask实现类的底层是基于接口回调技术实现,具体可参考:基于接口回调详解JUC中Callable和FutureTask实现原理

 1 public class TestCallable implements Callable<Integer>{
 2     //实现Callable并重写call方法作为线程执行体, 并设置返回值1
 3     @Override
 4     public Integer call() throws Exception {
 5         System.out.println("Thread is running...");
 6         Thread.sleep(3000);
 7         return 1;
 8     }
 9     
10     public static void main(String[] args) throws InterruptedException, ExecutionException {
11         //创建Callable实现类的对象
12         TestCallable tc = new TestCallable();
13         //创建FutureTask类的对象
14         FutureTask<Integer> task = new FutureTask<>(tc);
15         //把FutureTask实现类对象作为target,通过Thread类对象启动线程
16         new Thread(task).start();    
17         System.out.println("do something else...");
18         //通过get方法获取返回值
19         Integer integer = task.get();    
20         System.out.println("The thread running result is :" + integer);    
21     }
22 }

总结一下,虽然继承Thread类的开发方式相对简单,但因为Java单继承的限制,一般建议通过实现Runnable或Callable接口来创建并启动多线程。

目录
相关文章
|
11月前
|
Java 编译器 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【9月更文挑战第37天】在Java的世界里,内存模型是隐藏在代码背后的守护者,它默默地协调着多线程环境下的数据一致性和可见性问题。本文将揭开Java内存模型的神秘面纱,带领读者探索其对并发编程实践的深远影响。通过深入浅出的方式,我们将了解内存模型的基本概念、工作原理以及如何在实际开发中正确应用这些知识,确保程序的正确性和高效性。
|
9月前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
125 0
|
10月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
765 6
|
10月前
|
设计模式 安全 Java
Java 多线程并发编程
Java多线程并发编程是指在Java程序中使用多个线程同时执行,以提高程序的运行效率和响应速度。通过合理管理和调度线程,可以充分利用多核处理器资源,实现高效的任务处理。本内容将介绍Java多线程的基础概念、实现方式及常见问题解决方法。
358 1
|
10月前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
127 2
|
12月前
|
Java 开发者
深入探索Java中的并发编程
本文将带你领略Java并发编程的奥秘,揭示其背后的原理与实践。通过深入浅出的解释和实例,我们将探讨Java内存模型、线程间通信以及常见并发工具的使用方法。无论是初学者还是有一定经验的开发者,都能从中获得启发和实用的技巧。让我们一起开启这场并发编程的奇妙之旅吧!
86 5
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
12月前
|
算法 安全 Java
Java中的并发编程是如何实现的?
Java中的并发编程是通过多线程机制实现的。Java提供了多种工具和框架来支持并发编程。
77 1
|
安全 Java 测试技术
掌握Java的并发编程:解锁高效代码的秘密
在Java的世界里,并发编程就像是一场精妙的舞蹈,需要精准的步伐和和谐的节奏。本文将带你走进Java并发的世界,从基础概念到高级技巧,一步步揭示如何编写高效、稳定的并发代码。让我们一起探索线程池的奥秘、同步机制的智慧,以及避免常见陷阱的策略。
|
C# 开发者 数据处理
WPF开发者必备秘籍:深度解析数据网格最佳实践,轻松玩转数据展示与编辑大揭秘!
【8月更文挑战第31天】数据网格控件是WPF应用程序中展示和编辑数据的关键组件,提供排序、筛选等功能,显著提升用户体验。本文探讨WPF中数据网格的最佳实践,通过DevExpress DataGrid示例介绍其集成方法,包括添加引用、定义数据模型及XAML配置。通过遵循数据绑定、性能优化、自定义列等最佳实践,可大幅提升数据处理效率和用户体验。
190 0