目录
一、Java线程介绍
在Java中,线程(Thread)是程序执行的最小单元,它允许程序在同一时间执行多个任务。Java中的线程可以由Thread类创建,也可以通过实现Runnable接口或Callable接口创建。下面是一些关于Java中线程的重要特性和用法:
1.1 创建线程
- 使用Thread类的构造方法或者实现Runnable接口来创建线程。
- 使用Callable接口和FutureTask类也可以创建线程,并且可以获取线程执行的结果。
1.2 启动线程
- 使用Thread类的start()方法来启动一个新线程,使得线程处于就绪状态并开始执行run()方法中的任务。
- 也可以通过Executor框架来启动线程,它提供了更加灵活和可控的线程管理方式。
1.3 线程生命周期
- 线程的生命周期包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)、超时等待(Timed Waiting)和终止(Terminated)等状态。
- 线程在不同的状态之间转换,受到操作系统和JVM的调度控制。
1.4 线程同步和互斥
- 多个线程访问共享资源时,可能会产生竞态条件(Race Condition)和数据不一致的问题。
- 可以使用同步机制(如synchronized关键字、Lock接口)来保证线程之间的互斥访问共享资源,以避免数据的不一致性。
1.5 线程通信
- 多个线程之间可能需要进行通信,以协调各自的工作。
- 可以使用wait()、notify()、notifyAll()等方法来实现线程之间的等待和通知机制,以及使用并发集合(如BlockingQueue)来实现线程安全的数据交换。
1.6 线程池
- 线程池是一种管理线程的机制,它可以有效地管理和重用线程,降低线程创建和销毁的开销,提高程序的性能和资源利用率。
- Java提供了Executor框架和ThreadPoolExecutor类来实现线程池的创建和管理。
1.7 线程安全
- 当多个线程同时访问共享资源时,需要考虑线程安全问题,以避免数据的不一致性和线程间的竞态条件。
- 可以使用同步机制、并发集合和线程安全的类来确保程序的线程安全性。
二、创建线程的5种方式
在Java中,创建线程的方式有以下几种:
2.1 实现Runnable接口
- 可以通过实现Runnable接口来创建线程,然后将实现了Runnable接口的对象作为参数传递给Thread类的构造方法。
- 示例代码如下:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running...");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // 启动线程
}
}
2.2 实现Callable接口
- 当我们需要创建一个可以返回结果的线程时,就可以使用实现了Callable接口的方式。Callable接口是在Java 5中引入的,它允许线程执行一个任务并返回一个结果,与Runnable接口相比,Callable接口的call()方法可以返回结果并抛出异常。
- 示例代码如下:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
public String call() {
return "Thread is running...";
}
}
public class Main {
public static void main(String[] args) {
// 创建Callable实现类的实例
Callable<String> callable = new MyCallable();
// 创建FutureTask对象,用于包装Callable对象
FutureTask<String> futureTask = new FutureTask<>(callable);
// 创建线程并启动
Thread thread = new Thread(futureTask);
thread.start();
try {
// 获取线程执行结果
String result = futureTask.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 继承Thread类
- 可以通过继承Thread类来创建线程,然后重写run()方法来定义线程执行的任务。
- 示例代码如下:
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running...");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
2.4 使用匿名内部类
- 可以使用匿名内部类来创建线程,这种方式通常在线程的代码较为简单时使用。
- 示例代码如下:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("Thread is running...");
}
});
thread.start(); // 启动线程
}
}
2.5 使用Lambda表达式(Java 8及以上版本)
- 在Java 8及以上版本中,可以使用Lambda表达式来简化创建线程的代码。
- 示例代码如下:
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running...");
});
thread.start(); // 启动线程
}
}
三、Runnable接口和Callable接口的区别
Runnable和Callable都是用于创建线程任务的接口,它们之间的主要区别在于以下几点:
3.1 返回值
- Runnable接口的run()方法没有返回值,因此线程任务无法返回执行结果。
- Callable接口的call()方法可以返回执行结果,并且可以抛出受检查的异常。
3.2 异常处理
- Runnable接口的run()方法不能抛出受检查的异常,只能抛出非受检查的RuntimeException。
- Callable接口的call()方法可以抛出任何类型的异常,包括受检查的异常。
3.3 兼容性
- Callable接口是在Java 5中引入的新接口,而Runnable接口是在Java 1.0中就存在的。
- Callable接口提供了更多的灵活性和功能,但Runnable接口仍然是使用较多的接口之一,因为它的简单性和兼容性。
3.4 并发集合
- Callable接口通常与ExecutorService和Future配合使用,以支持异步任务执行和获取结果。
- Runnable接口通常与Thread类或者Executor框架一起使用,用于执行简单的线程任务。
总的来说,如果需要线程执行任务并返回结果,以及处理受检查的异常,那么可以使用Callable接口;如果只是需要执行简单的线程任务,而不需要返回结果或处理异常,那么可以使用Runnable接口。