Java之线程篇三:线程的同步与通讯
在多线程编程中,线程的同步与通讯是确保线程安全和良好协作的关键东西。本篇文章将介绍Java中线程同步的基本概念、常用的同步工具、以及线程之间的通讯机制。
线程同步的基本概念
在多线程环境下,多个线程可能会同时访问共享资源,如变量、对象等。这可能导致数据的不一致性或状态错误。例如,两个线程同时对一个变量进行写操作,如果没有正确的同步措施,最终结果可能会出错。因此,线程同步就是为了避免以上情况的发生。
常用的同步机制
Java提供了多种机制来实现线程同步,最常用的包括:synchronized关键字、Lock接口以及ReentrantLock类。
1. 使用synchronized关键字
synchronized
关键字可以修饰方法或代码块,确保同一时刻只有一个线程可以访问被修饰的代码。
class Counter {
private int count = 0;
// 使用synchronized修饰方法
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SyncExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Counter: " + counter.getCount());
}
}
在上述例子中,increment
方法被synchronized
修饰,因此当一个线程在执行这个方法时,其他线程将无法同时访问该方法,从而确保了count
变量的线程安全。
2. 使用Lock接口
ReentrantLock
是Java提供的一种更灵活的线程同步机制,可以替代synchronized
关键字。它提供了比synchronized更高级的功能,例如可中断的锁、定时锁等。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class CounterLock {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 上锁
try {
count++;
} finally {
lock.unlock(); // 解锁
}
}
public int getCount() {
return count;
}
}
public class LockExample {
public static void main(String[] args) throws InterruptedException {
CounterLock counter = new CounterLock();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Counter: " + counter.getCount());
}
}
在这个例子中,我们使用ReentrantLock
来实现线程安全。通过lock和unlock方法手动控制锁的获取与释放,这种方式更灵活,也能避免死锁的发生。
线程通讯
线程通讯通常是指一个线程影响另一个线程的执行,最常用的方式是使用wait()
、notify()
和notifyAll()
方法,它们都是在对象的监视器上进行操作。
下面是一个简单的生产者-消费者示例:
class SharedResource {
private int count;
private final int LIMIT = 10;
public synchronized void produce() throws InterruptedException {
while (count == LIMIT) {
wait(); // 等待
}
count++;
System.out.println("Produced: " + count);
notifyAll(); // 通知消费者
}
public synchronized void consume() throws InterruptedException {
while (count == 0) {
wait(); // 等待
}
count--;
System.out.println("Consumed: " + count);
notifyAll(); // 通知生产者
}
}
public class ProducerConsumerExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Runnable producer = () -> {
for (int i = 0; i < 20; i++) {
try {
resource.produce();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
Runnable consumer = () -> {
for (int i = 0; i < 20; i++) {
try {
resource.consume();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
};
new Thread(producer).start();
new Thread(consumer).start();
}
}
在这个示例中,我们创建了一个SharedResource
类,其中包含了生产和消费的逻辑。使用wait()
和notifyAll()
实现了生产者和消费者间的通讯。当缓冲区满时,生产者会等待;当缓冲区为空时,消费者会等待。
总结
Java的线程同步与通讯是多线程编程中非常重要的组成部分。通过合理使用synchronized、Lock以及wait/notify机制,可以有效地实现线程间的协作与资源的安全访问。随着对多线程编程理解的加深,开发者能够更加自如地应对各种复杂的同步场景。