进程间同步
1. 概念
进程是程序在操作系统中的一次执行过程,每个进程都有自己独立的内存空间和系统资源。进程间同步是指多个进程在访问共享资源或进行协作时,按照一定规则协调执行顺序,以保证数据一致性和程序正确性。
2. 同步原因
多个进程可能同时访问和修改共享资源,如共享文件、共享内存区域等,这会导致竞争条件和数据不一致问题。例如,多个进程同时对一个共享文件进行写操作,可能会导致文件内容混乱。
3. 同步方法
- 信号量(Semaphore)
- 原理:信号量是一个计数器,用于控制对共享资源的访问。进程可以通过
P
操作(申请资源,计数器减 1)和V
操作(释放资源,计数器加 1)来获取和释放资源。当计数器小于 0 时,进程会被阻塞。 - Java 示例:在 Java 中没有直接的信号量用于进程间同步,但可以借助操作系统的机制或第三方库。在 Linux 下可以使用 JNI(Java Native Interface)调用系统的信号量函数。以下是一个简单示意:
- 原理:信号量是一个计数器,用于控制对共享资源的访问。进程可以通过
// 假设使用 JNI 调用系统信号量函数
public class SemaphoreProcessSync {
static {
System.loadLibrary("semaphore_jni");
}
// 声明本地方法
public native void semaphoreP();
public native void semaphoreV();
public static void main(String[] args) {
SemaphoreProcessSync sync = new SemaphoreProcessSync();
// 模拟进程操作
sync.semaphoreP();
try {
// 临界区代码
System.out.println("Process is in critical section.");
} finally {
sync.semaphoreV();
}
}
}
- 消息队列(Message Queue)
- 原理:消息队列是一种在进程之间传递消息的机制。进程可以将消息发送到队列中,也可以从队列中接收消息,实现进程间的异步通信。
- Java 示例:可以使用 Java 的
java.net
包通过网络实现类似消息队列的功能,以下是一个简单的基于 TCP 套接字的消息传递示例:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
// 服务器端进程
class Server {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
System.out.println("Server is listening on port 12345");
Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = in.readLine();
System.out.println("Received message: " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 客户端进程
class Client {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
out.println("Hello from client!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 共享内存(Shared Memory)
- 原理:多个进程可以访问同一块物理内存区域,从而实现数据的共享。进程可以直接在共享内存中读写数据,提高数据传输效率。
- Java 示例:Java 中可以通过
java.nio
包的MappedByteBuffer
实现文件映射到内存,模拟共享内存。以下是一个简单示例:
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class SharedMemoryExample {
public static void main(String[] args) {
try (RandomAccessFile file = new RandomAccessFile("shared_memory.txt", "rw");
FileChannel channel = file.getChannel()) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
// 写入数据
buffer.put("Hello, shared memory!".getBytes());
// 读取数据
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println(new String(data));
} catch (Exception e) {
e.printStackTrace();
}
}
}
线程间同步
1. 概念
线程是进程内的执行单元,同一进程内的多个线程共享进程的资源。线程间同步是指在一个进程内,多个线程在访问共享资源或进行协作时,采取措施协调执行顺序,避免数据竞争和不一致问题。
2. 同步原因
多个线程同时访问和修改共享资源时,会导致数据不一致和竞争条件。例如,多个线程同时对一个共享变量进行自增操作,可能会导致最终结果不符合预期。
3. 同步方法
synchronized
关键字- 原理:
synchronized
关键字可以修饰方法或代码块,当一个线程访问被synchronized
修饰的代码时,会自动获取对象的锁,其他线程必须等待该线程释放锁才能访问。 - Java 示例:
- 原理:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizedExample {
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());
}
}
ReentrantLock
类- 原理:
ReentrantLock
是 Java 提供的可重入锁,功能比synchronized
更强大,提供了更多的方法来控制锁的获取和释放。 - Java 示例:
- 原理:
import java.util.concurrent.locks.ReentrantLock;
class CounterWithLock {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
public class ReentrantLockExample {
public static void main(String[] args) throws InterruptedException {
CounterWithLock counter = new CounterWithLock();
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());
}
}
Semaphore
类- 原理:
Semaphore
用于控制同时访问某个资源的线程数量。线程可以通过acquire()
方法获取许可,通过release()
方法释放许可。 - Java 示例:
- 原理:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2); // 允许同时访问的线程数为 2
Runnable task = () -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " acquired the semaphore.");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + " released the semaphore.");
}
};
for (int i = 0; i < 5; i++) {
new Thread(task).start();
}
}
}