在Java并发编程中,多线程同步和互斥是非常重要的概念。为了保证程序的正确性和性能,我们需要使用多种方式来实现多线程之间的同步和互斥。
一、synchronized关键字
synchronized关键字是Java中最常用的实现多线程同步和互斥的方法之一。通过给某个对象或方法添加synchronized修饰符可以保证多个线程之间的互斥性,即当一个线程获得某个对象的锁时,其他线程必须等待该线程释放该锁才能够执行对应代码块。此外,synchronized还具有可见性,即当一个线程释放锁时,会将锁变量的值刷新到主存储器中,从而使其它线程可以感知到锁状态的变化。
示例如下:
public class Test {
private static int count = 0;
public static synchronized void add() {
count++;
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count); //输出20000
}
}
在上述示例中,我们定义了一个静态变量count,并通过synchronized关键字修饰add()方法实现对count的访问同步和互斥。最终输出结果为20000。
二、ReentrantLock类
ReentrantLock是Java.util.concurrent包中提供的另一种实现互斥性的方式。与synchronized相比,ReentrantLock具有更加灵活的控制锁的机制,可以支持选择公平锁或者非公平锁等高级特性。
public class Test {
private static int count = 0;
private final static Lock lock = new ReentrantLock(true);
public static void add() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count); //输出20000
}
}
上述示例中,在add()方法中,我们使用ReentrantLock类来实现线程之间的互斥访问。并且通过lock()和unlock()方法手动获取和释放该对象的锁status。最终输出结果与前一个示例代码一致。
三、AtomicInteger类
对于只需要进行基本计数的场景,可以使用Java中的原子类,例如AtomicInteger。由于其底层实现是基于CAS(Compare And Swap)原语实现的,所以不需要显式地加锁,也可以保证线程之间的互斥性和可见性。
public class Test {
private static AtomicInteger count = new AtomicInteger(0);
public static void add() {
count.getAndIncrement();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
add();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count); //输出20000
}
}
上述示例代码中,我们定义了一个AtomicInteger对象来实现对count的自增操作,输出结果与前两个示例相同。
总之,在Java并发编程中,为了保证多线程之间的正确性和性能,我们需要使用多种方式来实现多线程同步和互斥,常用的包括synchronized关键字、ReentrantLock类和原子类等。我们应该根据具体应用场景选择合适的方法来确保程序执行的正确性和高效性。