并发编程进阶:线程同步与互斥
探讨线程同步的重要性,介绍互斥锁(mutex)、条件变量等同步机制。
并发编程进阶:线程同步与互斥
一、线程同步的重要性
线程同步是多线程编程中的一个核心概念,它指的是在多线程环境中,通过一定的机制保证多个线程按照某种特定的方式正确、有序地执行。这主要是为了避免并发问题,如死锁、竞态条件、资源争用等,确保数据的一致性和完整性。
当多个线程共享同一份资源时,由于线程的执行顺序是不确定的,可能会出现线程安全问题。例如,两个线程同时对一个共享变量进行操作,可能会出现预期之外的结果。为了避免这种情况,就需要对线程进行同步,即保证同一时刻只有一个线程可以对共享资源进行操作。
在Java中,同步代码块和同步方法是实现线程同步的常用方式。同步代码块通过synchronized关键字和一个锁对象来实现,而同步方法则是在方法声明前加上synchronized关键字,以确保该方法在任何时刻只被一个线程访问。
二、互斥锁(Mutex)
互斥锁(Mutex)是一种用于多线程编程的同步原语,用于确保在多个线程访问共享资源时的互斥性。它的主要作用是保护共享资源,防止多个线程同时访问导致的数据竞争和不一致问题。
互斥锁的工作原理:
加锁(Lock):当一个线程需要访问共享资源时,它会尝试获取互斥锁。如果互斥锁当前没有被其他线程持有,那么该线程就能够获取互斥锁,继续执行访问共享资源的代码。如果互斥锁已经被其他线程持有,那么该线程会进入阻塞状态,等待互斥锁的释放。
解锁(Unlock):当一个线程完成对共享资源的访问时,它会释放互斥锁,以便其他线程可以获取互斥锁并访问共享资源。
在C++中,std::mutex是标准库提供的互斥锁实现,而在Java中,虽然Java本身没有直接提供名为Mutex的类,但synchronized关键字和java.util.concurrent.locks.Lock接口及其实现类(如ReentrantLock)都提供了类似的功能。
三、条件变量
条件变量是另一种重要的线程同步机制,它使线程能够等待某个条件的发生。条件变量通常与互斥锁一起使用,以避免竞态条件。
条件变量的工作原理:
等待(Wait):一个线程在获取互斥锁后,如果发现某个条件不满足,它可以调用条件变量的wait方法进入等待状态,并释放互斥锁。这样,其他线程就可以获取互斥锁并修改条件。
通知(Notify):当条件被满足时,另一个线程会调用条件变量的notify或notifyAll方法来唤醒一个或所有等待的线程。被唤醒的线程会重新尝试获取互斥锁,并在获取锁后检查条件是否确实满足。
条件变量主要用于实现线程间的协作,使线程能够按照某种特定的顺序或条件来执行。在C语言中,条件变量通过pthread_cond_t类型来表示,而在Java中,则没有直接的条件变量类型,但可以通过Object类的wait、notify和notifyAll方法,或者在java.util.concurrent.locks包中的Condition接口来实现类似的功能。
总结
线程同步是并发编程中的关键问题,它关系到程序的正确性和性能。互斥锁和条件变量是两种重要的线程同步机制,它们分别用于保护共享资源和实现线程间的协作。在实际编程中,应根据具体需求选择合适的同步机制,并合理设计同步策略,以确保程序的正确性和高效性。
并发编程进阶:线程同步与互斥的深入实践
一、线程同步的代码实现
在Java中,线程同步的核心在于使用synchronized关键字或java.util.concurrent.locks包中的锁机制。以下将通过具体代码示例展示这些同步方法的应用。
1. 使用synchronized关键字
同步方法示例:
public class Counter { |
private int count = 0; |
|
// 同步方法 |
public synchronized void increment() { |
count++; |
} |
|
public synchronized int getCount() { |
return count; |
} |
} |
|
// 使用示例 |
public class Main { |
public static void main(String[] args) throws InterruptedException { |
Counter counter = new Counter(); |
Thread t1 = new Thread(() -> { |
for (int i = 0; i < 1000; i++) { |
counter.increment(); |
} |
}); |
|
Thread t2 = new Thread(() -> { |
for (int i = 0; i < 1000; i++) { |
counter.increment(); |
} |
}); |
|
t1.start(); |
t2.start(); |
|
t1.join(); |
t2.join(); |
|
System.out.println("Final count: " + counter.getCount()); // 期望输出2000 |
} |
} |
同步代码块示例:
public class SafeList { |
private List<Integer> list = new ArrayList<>(); |
private final Object lock = new Object(); |
|
public void add(int value) { |
synchronized(lock) { |
list.add(value); |
} |
} |
|
public int get(int index) { |
synchronized(lock) { |
return list.get(index); |
} |
} |
} |
|
// 使用示例略 |
2. 使用java.util.concurrent.locks.Lock接口
ReentrantLock是Lock接口的一个实现,提供了比synchronized更灵活的锁定操作。
import java.util.concurrent.locks.Lock; |
import java.util.concurrent.locks.ReentrantLock; |
|
public class CounterWithLock { |
private int count = 0; |
private final Lock lock = new ReentrantLock(); |
|
public void increment() { |
lock.lock(); |
try { |
count++; |
} finally { |
lock.unlock(); |
} |
} |
|
public int getCount() { |
lock.lock(); |
try { |
return count; |
} finally { |
lock.unlock(); |
} |
} |
} |
|
// 使用示例与上述synchronized示例类似 |
二、互斥锁(Mutex)的深入应用
在C++中,std::mutex是标准库提供的互斥锁实现,其用法直观且高效。
#include <iostream> |
#include <mutex> |
#include <thread> |
|
std::mutex mtx; |
|
void print_block(int n, char c) { |
mtx.lock(); |
for (int i = 0; i < n; ++i) { std::cout << c; } |
std::cout << '\n'; |
mtx.unlock(); |
} |
|
int main() { |
std::thread th1(print_block, 50, '*'); |
std::thread th2(print_block, 50, '$'); |
|
th1.join(); |
th2.join(); |
|
return 0; |
} |
三、条件变量的详细实现
在Java中,虽然没有直接的条件变量类型,但可以通过Object的wait(), notify(), notifyAll()方法或java.util.concurrent.locks.Condition接口来实现。
使用Object的wait()和notify()方法:
public class WaitNotifyExample { |
private final Object lock = new Object(); |
private boolean ready = false; |
|
public void waitForReady() throws InterruptedException { |
synchronized(lock) { |
while (!ready) { |
lock.wait(); |
} |
// 执行操作 |
} |
} |
|
public void setReady() { |
synchronized(lock) { |
ready = true; |
lock.notify(); // 唤醒一个等待的线程 |
} |