Java中的多线程函数:如何实现线程互斥?
在Java中,实现线程互斥主要有以下几种方式:
1. 使用synchronized关键字:synchronized关键字可以将一个方法或一个代码块标记为临界区,保证同一时间只能有一个线程执行该区域的代码。当一个线程进入synchronized代码块时,其他线程会被阻塞,直到进入线程执行完临界区的代码。
示例代码:
public class MutexExample {
private static int counter = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (MutexExample.class) { // 使用类对象作为锁
for (int i = 0; i < 1000; i++) {
counter++;
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (MutexExample.class) { // 使用类对象作为锁
for (int i = 0; i < 1000; i++) {
counter--;
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter: " + counter);
}
}
在上述代码中,使用synchronized关键字将线程1和线程2中的临界区代码块进行了同步,保证了对counter变量的互斥访问。
2. 使用ReentrantLock类:ReentrantLock是Java提供的一个显示锁(独占锁),可以使用lock()方法获得锁,使用unlock()方法释放锁。与synchronized关键字相比,ReentrantLock提供了更加灵活的锁定机制,可以支持可重入性、公平性等特性。
示例代码:
public class MutexExample {
private static int counter = 0;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
lock.lock();
try {
for (int i = 0; i < 1000; i++) {
counter++;
}
} finally {
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
lock.lock();
try {
for (int i = 0; i < 1000; i++) {
counter--;
}
} finally {
lock.unlock();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter: " + counter);
}
}
在上述代码中,使用ReentrantLock类的lock()和unlock()方法来替代了synchronized关键字。使用try-finally语句块是为了保证锁的释放,在finally块中调用unlock()方法来释放锁。
3. 使用Semaphore类:Semaphore是Java提供的一个计数信号量,它可以控制同时访问某个资源的线程数量。当线程要访问该资源时,首先要从Semaphore获取许可证,如果许可证数小于等于0,线程会被阻塞。当线程访问完毕后,要归还许可证给Semaphore。
示例代码:
public class MutexExample {
private static int counter = 0;
private static Semaphore semaphore = new Semaphore(1);
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
semaphore.acquire();
for (int i = 0; i < 1000; i++) {
counter++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
Thread thread2 = new Thread(() -> {
try {
semaphore.acquire();
for (int i = 0; i < 1000; i++) {
counter--;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter: " + counter);
}
}
在上述代码中,使用Semaphore类来实现对counter变量的互斥访问。Semaphore的构造函数传入的参数表示许可证的数量,这里传入1表示同时只允许一个线程访问资源。acquire()方法用于获取许可证,如果没有许可证则会被阻塞,release()方法用于释放许可证。
总结:以上是在Java中实现线程互斥的几种方式,分别是使用synchronized关键字、ReentrantLock类和Semaphore类。在实际开发中,根据不同的需求和场景选择合适的方式来实现线程互斥,以确保多线程程序的正确性和数据的一致性。
