第2课_线程的同步和通信
热度🔥:30 免费课程
授课语音
Java多线程同步和通信
内容包括基本概念、同步与通信方法、常见线程安全问题的模拟、线程同步的不同实现方式,以及具体的代码示例
1. 线程的常用方法
sleep(long millis)
:让线程进入休眠状态,暂停指定时间。join()
:让当前线程等待另一个线程执行完毕。yield()
:提示让出CPU资源,但不保证实际效果,可能立即再次执行。interrupt()
:中断线程的执行。isAlive()
:判断线程是否还在运行。
代码示例:使用 sleep
和 join
public class ThreadMethodsDemo {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
System.out.println("线程1执行次数: " + i);
try {
Thread.sleep(500); // 线程暂停500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
try {
thread1.join(); // 等待线程1完成后再执行
System.out.println("线程2开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
}
2. 线程安全问题的模拟
- 当多个线程同时访问和修改共享资源(如变量或对象)时,如果没有适当的同步措施,就可能导致数据不一致的情况。
代码示例:线程安全问题模拟
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 非线程安全的操作
}
public int getCount() {
return count;
}
public static void main(String[] args) {
UnsafeCounter counter = new UnsafeCounter();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终计数值: " + counter.getCount());
}
}
在该示例中,由于 increment
方法是非同步的,可能导致线程间计数值不一致。
3. 线程同步
- 线程同步用于解决线程安全问题,保证多个线程对共享资源的操作是有序的。常见同步方式包括:
- 同步代码块
- 同步方法
- Lock锁
3.1 同步代码块
- 使用
synchronized
关键字,指定一个同步代码块,仅允许一个线程进入。
public class SynchronizedBlock {
private int count = 0;
public void increment() {
synchronized (this) { // 使用同步代码块,保证线程安全
count++;
}
}
public int getCount() {
return count;
}
}
3.2 同步方法
- 在方法前加
synchronized
关键字,使整个方法在同一时间内只能被一个线程执行。
public class SynchronizedMethod {
private int count = 0;
public synchronized void increment() { // 同步方法
count++;
}
public int getCount() {
return count;
}
}
3.3 使用 Lock 实现同步
- Lock 是一种显式的锁机制,提供更细粒度的锁控制。
- 特点:支持可重入锁、锁超时和非阻塞锁。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 确保在异常情况下释放锁
}
}
public int getCount() {
return count;
}
}
4. 线程通信
- 线程通信用于解决线程间的协调问题,Java提供了
wait()
、notify()
和notifyAll()
方法,常用于生产者-消费者模式。
代码示例:生产者-消费者问题
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int CAPACITY = 5;
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (this) {
while (queue.size() == CAPACITY) {
wait(); // 队列满时,等待消费者消费
}
queue.offer(value);
System.out.println("生产者生产了: " + value);
value++;
notifyAll(); // 通知消费者可以消费
}
}
}
public void consume() throws InterruptedException {
while (true) {
synchronized (this) {
while (queue.isEmpty()) {
wait(); // 队列为空时,等待生产者生产
}
int value = queue.poll();
System.out.println("消费者消费了: " + value);
notifyAll(); // 通知生产者可以继续生产
}
}
}
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producerThread = new Thread(() -> {
try {
pc.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread consumerThread = new Thread(() -> {
try {
pc.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producerThread.start();
consumerThread.start();
}
}
5. 多线程同步和通信的最佳实践
- 尽量使用同步代码块代替同步方法,避免锁定不必要的代码,提高性能。
- 避免死锁:多个线程互相等待资源,造成程序阻塞,需合理安排锁的获取顺序。
- 使用合适的锁机制:在需要更加灵活控制锁的释放与获取时,推荐使用
Lock
接口。 - 减少同步代码块的长度:同步块越小,越能减少对其他线程的影响。
- 合理利用线程通信:生产者-消费者模式非常适合解决线程协调问题,保证资源的平衡使用。
以上是关于Java多线程同步和通信的内容,涵盖了从基础概念到代码实现的各个方面。希望通过这些内容,您能深入理解Java多线程的同步和通信技巧,在实际应用中更好地处理并发问题,提高程序的安全性和效率。