Java中线程的通信与协作
发布时间: 2024-01-16 08:36:51 阅读量: 17 订阅数: 16 ![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
![](https://csdnimg.cn/release/wenkucmsfe/public/img/col_vip.0fdee7e1.png)
# 1. Java中线程基础
### 1.1 线程的概念与基本原理
在Java中,线程是程序中的执行单元。每个程序至少有一个主线程,主线程会按照程序的顺序依次执行代码。而通过创建多个线程,可以让程序同时执行多个任务,以达到并发执行的效果。
线程的基本原理是通过CPU的调度来实现的。在单核处理器中,多个线程会交替执行,每个线程只占用CPU的一小段时间,即所谓的时间片。而在多核处理器中,多个线程可以同时执行,每个线程独占一个CPU核心。
### 1.2 Java中的线程实现方式
在Java中,线程主要有两种实现方式:
- 继承Thread类:通过继承Thread类并重写run()方法来实现线程。
- 实现Runnable接口:通过实现Runnable接口,并将实现了run()方法的对象传入Thread类中来实现线程。
### 1.3 线程的生命周期与状态转换
线程在整个运行过程中会经历多个生命周期状态,包括新建、就绪、运行、阻塞和终止等状态。
- 新建状态:当创建了一个线程对象后,线程就处于新建状态。
- 就绪状态:线程处于就绪状态时,表示线程已经被创建,但还未被调度执行。
- 运行状态:线程获取到CPU时间片后,开始执行。
- 阻塞状态:线程在被等待某个条件时,会进入阻塞状态。
- 终止状态:线程执行完任务或发生异常时,会进入终止状态。
线程的状态转换遵循着特定的规则,如新建线程会经历就绪状态,就绪线程被调度后会进入运行状态,运行线程进入阻塞状态后可以再次进入就绪状态等等。
以上是Java中线程的基础知识。在接下来的章节中,我们将深入探讨线程间的通信与协作。
# 2. 线程间的通信方式
### 2.1 共享变量与内存可见性
在多线程编程中,线程间通信是一个重要的概念。线程之间的通信通常是通过共享变量来实现的。然而,要注意到线程之间共享变量的可见性问题。
在Java中,当多个线程同时操作一个共享变量时,由于线程之间具有各自的缓存,可能导致一个线程修改了共享变量的值,但其他线程并不能立刻看到这个修改。这就是所谓的内存可见性问题。
为了解决内存可见性问题,可以使用`volatile`关键字来修饰共享变量。`volatile`关键字可以确保被修饰的变量在多线程环境下的可见性,即一个线程修改了该变量的值,其他线程能够立刻看到这个修改。
下面是一个使用`volatile`关键字解决内存可见性问题的示例代码:
```java
public class SharedVariableExample {
private volatile boolean flag = false;
public void setFlag() {
flag = true;
}
public void printFlag() {
while (!flag) {
// 空循环
}
System.out.println("Flag is true.");
}
public static void main(String[] args) {
SharedVariableExample example = new SharedVariableExample();
Thread thread1 = new Thread(() -> {
example.setFlag();
});
Thread thread2 = new Thread(() -> {
example.printFlag();
});
thread1.start();
thread2.start();
}
}
```
在上述代码中,我们定义了一个`flag`变量,并使用`volatile`关键字修饰。线程`thread1`负责修改`flag`的值为`true`,而线程`thread2`则负责打印`flag`的值。由于`flag`使用了`volatile`关键字修饰,所以当`thread2`在循环中检查`flag`的值时,能够立刻看到`thread1`修改的结果。
### 2.2 使用synchronized实现线程间同步
除了使用`volatile`关键字修饰共享变量,我们还可以使用`synchronized`关键字来实现线程间的同步。
`synchronized`关键字可以修饰方法或代码块,用于确保在同一时间只有一个线程可以执行被修饰的代码。当一个线程获得了对象的锁时,其他线程必须等待这个线程释放锁才能继续执行。
下面是一个使用`synchronized`关键字实现线程间同步的示例代码:
```java
public class SynchronizedExample {
private synchronized void printNumber() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
public static void main(String[] args) {
SynchronizedExample example = new SynchronizedExample();
Thread thread1 = new Thread(() -> {
example.printNumber();
});
Thread thread2 = new Thread(() -> {
example.printNumber();
});
thread1.start();
thread2.start();
}
}
```
在上述代码中,我们定义了一个`printNumber`方法,并使用`synchronized`关键字修饰。两个线程`thread1`和`thread2`同时调用这个方法,由于方法被`synchronized`修饰,所以在任意时刻只有一个线程能够执行该方法。
### 2.3 使用Lock实现线程间同步
除了`synchronized`关键字,Java还提供了`Lock`接口和其实现类来实现线程间的同步。
`Lock`接口定义了获取锁和释放锁的方法,使用`Lock`可以更加灵活地控制线程的同步。
下面是一个使用`Lock`实现线程间同步的示例代码:
```java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private Lock lock = new ReentrantLock();
private void printNumber() {
lock.lock();
try {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
LockExample example = new LockExample();
Thread thread1 = new Thread(() -> {
example.printNumber();
});
Thread thread2 = new Thread(() -> {
example.printNumber();
});
thread1.start();
thread2.start();
}
}
```
在上述代码中,我们使用`ReentrantLock`类实现了`Lock`接口,并在`printNumber`方法中使用`lock`对象进行同步。两个线程同时调用`printNumber`方法时,只有一个线程能够获取到锁并执行方法中的代码块。
### 2.4 使用volatile关键字保证可见性
除了修饰共享变量保证可见性外,`volatile`关键字还可以用来禁止指令重排序。
在多线程环境下,为了提高执行效率,JVM可能会对指令进行重排序。然而,重排序有时可能会导致线程之间的依赖关系出现问题,造成程序逻辑错误。
使用`volatile`关键字修饰的变量,既保证了被修饰变量的可见性,又禁止了指令重排序。
下面是一个使用`volatile`关键字禁止指令重排序的示例代码:
```java
public class VolatileExample {
private volatile int count = 0;
public void increase() {
count++;
}
public static void main(String[] args) {
VolatileExample example = new VolatileExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increase();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increase();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Count: " + example.count);
}
}
```
在上述代码中,我们定义了一个`count`变量,并使用`volatile`关键字修饰。两个线程同时调用`increase`方法对`count`进行累加操作。由于`count`变量使用了`volatile`关键字修饰,这样能够确保在多线程环境下对`count`的操作不会发生指令重排序,保证了结果的正确性。
# 3. 线程的阻塞与唤醒
#### 3.1 等待通知机制
在多线程编程中,线程的阻塞与唤醒是实现线程间协作的重要手段之一。Java提供了等待通知机制,即通过使用`wait()`和`notify()`方法来实现线程的阻塞和唤醒。
- `wait()`方法:使线程进入等待状态,释放对象的锁,并等待其他线程调用`notify()`或`notifyAll()`方法来唤醒。
- `notify()`方法:随机唤醒等待中的一个线程,使其从等待状态转为可运行状态,但并不立即释放锁。
- `notifyAll()`方法:唤醒所有处于等待状态的线程。
当线程调用了`wait()`方法后,它会释放对象的锁,并进入等待队列中,直到其他线程调用了
0
0
相关推荐
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![pdf](https://img-home.csdnimg.cn/images/20210720083512.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)