一、引言
在Java编程中,多线程是一个非常重要的概念。它允许我们同时执行多个任务,从而提高程序的执行效率和响应速度。本文将介绍Java多线程的基本概念、创建方式以及线程间的同步与通信,并通过示例代码进行说明。
二、Java多线程基本概念
线程(Thread):线程是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以包含多个线程,它们共享进程的地址空间和系统资源。
并发(Concurrency):并发是指两个或多个线程在同一时间段内交替执行的过程。由于线程切换的时间非常短,因此从用户的角度来看,多个线程似乎是同时执行的。
并行(Parallelism):并行是指两个或多个线程在同一时刻内同时执行的过程。这通常需要多个处理器核心或超线程技术的支持。
三、Java多线程的创建方式
-
继承Thread类
通过继承Thread类并重写其run()方法,可以创建一个新的线程。在run()方法中编写线程要执行的代码。然后,通过调用线程的start()方法启动线程。
示例代码:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程 " + Thread.currentThread().getId() + " 执行第 " + i + " 次任务");
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}
-
实现Runnable接口
除了继承Thread类,Java还提供了另一种创建线程的方式,即实现Runnable接口。Runnable接口中只有一个run()方法,需要由实现类来重写。然后,将实现类的对象作为参数传递给Thread类的构造函数,创建Thread对象,并调用其start()方法启动线程。
示例代码:
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程 " + Thread.currentThread().getId() + " 执行第 " + i + " 次任务");
}
}
public static void main(String[] args) {
MyRunnable task = new MyRunnable();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
-
实现Callable接口并使用FutureTask
除了继承Thread类和实现Runnable接口,Java还提供了Callable接口来创建线程。Callable接口与Runnable接口相似,但Callable允许有返回值,并且可以抛出异常。为了使用Callable,我们需要将其封装成FutureTask对象,然后将其传递给Thread对象。
示例代码:
import java.util.concurrent .*;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable task = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread t1 = new Thread(futureTask);
t1.start();
// 等待线程执行完成并获取结果
Integer result = futureTask.get();
System.out.println("线程计算结果为:" + result);
}
}
在这个例子中,我们创建了一个实现了Callable接口的MyCallable类,它返回从0到99的整数和。然后,我们将MyCallable对象封装成FutureTask对象,并将FutureTask对象传递给Thread对象。当线程执行完成后,我们可以通过调用FutureTask的get()方法来获取线程的执行结果。
四、线程间的同步与通信
当多个线程访问共享资源时,可能会出现数据不一致的问题。为了解决这个问题,Java提供了多种同步机制,如synchronized关键字、Lock接口等。此外,线程间还可以通过等待/通知机制进行通信。
synchronized关键字
synchronized关键字可以用于修饰方法或代码块,以确保同一时间只有一个线程可以执行被修饰的代码。当一个线程进入synchronized方法或代码块时,其他尝试进入的线程将被阻塞,直到当前线程退出synchronized方法或代码块。
Lock接口
除了synchronized关键字,Java还提供了Lock接口来实现更灵活的同步控制。Lock接口提供了更丰富的同步方法,如lock()、unlock()、tryLock()等。此外,Lock接口还支持中断等待的线程、尝试获取锁等高级功能。
等待/通知机制详解
Java中的等待/通知机制是基于对象锁的,它允许线程之间进行协作。当一个线程需要等待某个条件成立时,它可以调用对象的wait()方法进入等待状态,并释放持有的锁。其他线程在修改条件后,可以调用对象的notify()或notifyAll()方法来唤醒等待的线程。 wait() 方法
当线程调用对象的wait()方法时,它会立即释放该对象的锁,并进入等待状态。
等待状态中的线程不会消耗CPU资源,直到被其他线程唤醒。
调用wait()方法后,线程必须重新获取锁才能继续执行。
notify() 方法
当一个线程调用对象的notify()方法时,它会随机唤醒该对象等待池中等待的一个线程(如果等待池中有线程的话)。
被唤醒的线程会重新尝试获取对象的锁,如果此时锁已被其他线程持有,则它将被阻塞,直到获得锁。
notifyAll() 方法
notifyAll()方法与notify()方法类似,但会唤醒等待池中等待的所有线程。
唤醒后的线程会按照竞争锁的规则重新竞争,即它们都需要重新获取对象的锁才能继续执行。
示例
下面是一个简单的示例,展示了如何使用等待/通知机制实现线程间的协作:
public class WaitNotifyExample { private int count = 0; private final Object lock = new Object();
public void increment() {
synchronized (lock) {
while (count == 5) {
try {
lock.wait(); // 当前线程等待,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println("Count: " + count);
lock.notifyAll(); // 唤醒等待的线程
}
}
public void decrement() {
synchronized (lock) {
while (count == 0) {
try {
lock.wait(); // 当前线程等待,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println("Count: " + count);
lock.notifyAll(); // 唤醒等待的线程
}
}
public static void main(String[] args) {
WaitNotifyExample example = new WaitNotifyExample();
// 创建两个线程,一个增加count,一个减少count
Thread incrementThread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
example.increment();
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread decrementThread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
example.decrement();
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
incrementThread.start();
decrementThread.start();
}
}
在这个示例中,我们创建了一个WaitNotifyExample类,它包含一个计数器count和一个锁对象lock。increment()方法用于增加count的值,decrement()方法用于减少count的值。在修改count的值之前,两个方法都会检查count的值,如果达到了某个条件(count == 5或count == 0),则调用wait()方法使当前线程等待,并释放锁。其他线程在修改count的值后,会调用notifyAll()方法唤醒等待的线程。这样就实现了线程间的协作和同步。
五、总结
本文介绍了Java多线程的基本概念、三种主要的创建方式(继承Thread类、实现Runnable接口、实现Callable接口并使用FutureTask)以及线程间的同步与通信。在实际开发中,我们需要根据具体需求选择合适的线程创建方式和同步机制,以确保程序的正确性和性能。特别是当线程需要返回结果时,使用Callable接口和FutureTask对象是一个很好的选择。