Java多线程并发编程(互斥锁Reentrant Lock)攻略
概述
在Java多线程编程中,为了保证多个线程并发执行时的安全性,我们需要使用同步控制。在Java中,synchronized关键字可以实现同步控制,但是它存在一些不足之处,比如它的锁只能是内置锁,无法进行灵活的控制和管理等。
为了解决这些问题,Java提供了一个更加灵活、功能更为强大的锁机制,即Reentrant Lock(重入锁),它可以实现可重入锁、公平锁、可中断锁等功能,被广泛用于Java多线程编程中。本文将详细介绍Java中的Reentrant Lock的相关知识点和用法。
Reentrant Lock的基本用法
1. 创建Reentrant Lock对象
在使用Reentrant Lock之前,我们需要先创建一个Reentrant Lock对象。创建方式如下:
Lock lock = new ReentrantLock();
2. 获取锁
在需要同步控制的代码块中,我们可以使用Reentrant Lock来获取锁,即调用lock()方法,如下所示:
lock.lock();
try {
// 需要同步控制的代码块
} finally {
lock.unlock();
}
在这里,我们使用try-finally代码块来确保获取锁成功后一定会释放锁,以免发生死锁。
3. 释放锁
在同步控制代码块执行完毕后,我们需要释放锁,即调用unlock()方法,如下所示:
lock.unlock();
示例1:使用Reentrant Lock保证线程安全
下面通过一个示例来说明如何使用Reentrant Lock保证多线程并发执行的安全性。
假设我们有一个计数器类Counter,它的两个方法分别用来增加和获取计数器的值,如下所示:
public class Counter {
private int count;
public void increment() {
count++; //计数器自增
}
public int getCount() {
return count; //获取计数器的值
}
}
现在我们需要在多线程并发执行的场景下,保证计数器的值能够正确累加。我们可以使用Reentrant Lock来实现该功能,具体代码如下所示:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count;
private Lock lock = new ReentrantLock(); //创建锁对象
public void increment() {
lock.lock(); //获取锁
try {
count++; //计数器自增
} finally {
lock.unlock(); //释放锁
}
}
public int getCount() {
return count; //获取计数器的值
}
}
在这里,我们使用了Reentrant Lock来保证多线程并发执行时的安全性,其中increment()方法用来对计数器进行自增操作,使用了Reentrant Lock获取锁,以确保每次只有一个线程在执行自增操作。而getCount()方法则是用来获取计数器的值,没有使用Reentrant Lock,因为这里不需要同步控制。
示例2:使用Reentrant Lock实现公平锁
在Reentrant Lock中,我们还可以设置锁的公平性。如果对于多个等待锁的线程来说,公平锁会按照先进先出的顺序获得锁,而非公平锁则不保证这一点。
我们可以通过在创建Reentrant Lock时,传入一个boolean类型的参数,来控制锁的公平性。默认情况下,Reentrant Lock是非公平锁。如果我们需要创建公平锁,则需要将该参数设置为true。
具体示例代码如下所示:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairLockDemo {
private final Lock lock;
public FairLockDemo(boolean fair) {
lock = new ReentrantLock(fair); //创建锁对象,并设置公平性
}
public void print() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得了锁");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
FairLockDemo fairLockDemo = new FairLockDemo(true); //创建公平锁
for (int i = 0; i < 10; i++) {
new Thread(() -> {
fairLockDemo.print(); //执行同步代码块
}).start();
}
}
}
在这里,我们创建了一个FairLockDemo类,其中包含一个print()方法。在该方法中,我们使用Reentrant Lock获取锁,并输出当前线程名字。
如果我们将锁设置为公平锁,那么输出结果应该会按照先进先出的顺序,即多个线程获得锁的顺序与它们进入等待队列的顺序保持一致。
Reentrant Lock的高级用法
1. 可重入锁
在Java中,一个线程获取了锁后,如果再次尝试获取锁,则会自动获取到锁。这种机制称之为可重入锁,也叫递归锁。
Reentrant Lock是可重入锁的一种实现,在程序执行过程中,同一个线程可以多次获得同一把锁,而且这些锁可以重复释放,如下所示:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final Lock lock = new ReentrantLock();
public void print() {
lock.lock(); //获取锁
try {
System.out.println(Thread.currentThread().getName() + "获得了锁");
increment(); //递归调用
} finally {
lock.unlock(); //释放锁
}
}
public void increment() {
lock.lock(); //获取锁
try {
System.out.println(Thread.currentThread().getName() + "获得了锁");
} finally {
lock.unlock(); //释放锁
}
}
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
new Thread(() -> {
demo.print(); //执行同步代码块
}).start();
}
}
在这个示例中,我们定义了一个ReentrantLockDemo类,其中包含一个print()方法和一个increment()方法,它们都使用了Reentrant Lock来获取锁。
在print()方法中,我们先输出当前线程名字,然后调用increment()方法,同时再次获取锁。在increment()方法中,我们同样输出当前线程名字,然后释放锁。
在执行过程中,我们会发现同一个线程可以重复获取锁,并且多次释放锁,这就说明我们使用了可重入锁。
2. 可中断锁
在Java多线程编程中,有时候需要让线程在等待锁时,可以被中断。Reentrant Lock可以支持可中断锁,即线程在等待锁的时候,能够响应中断。
我们可以通过调用lockInterruptibly()方法来实现可中断锁,代码如下所示:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class InterruptibleLockDemo {
private final Lock lock = new ReentrantLock();
public void print() {
try {
lock.lockInterruptibly(); //获取可中断锁
try {
System.out.println(Thread.currentThread().getName() + "获得了锁");
} finally {
lock.unlock(); //释放锁
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
public static void main(String[] args) {
InterruptibleLockDemo demo = new InterruptibleLockDemo();
Thread thread1 = new Thread(() -> {
demo.print(); //执行同步代码块
});
thread1.start();
Thread thread2 = new Thread(() -> {
demo.print(); //执行同步代码块
});
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt(); //中断线程2
}
}
在这里,我们定义了一个InterruptibleLockDemo类,其中包含一个print()方法,调用lockInterruptibly()方法来获取可中断锁。
在main()方法中,我们先启动两个线程,分别执行print()方法来获取锁。然后暂停1秒钟后,中断线程2。在执行过程中,线程1先获取到了锁,而线程2却被挂起了,而且在中断后输出了“线程2被中断了”的提示信息,说明我们成功地使用了可中断锁。
总结
本文基于Java多线程编程中的同步问题,介绍了Reentrant Lock的相关知识点和用法,包括Reentrant Lock的基本用法、可重入锁、公平锁和可中断锁等。Reentrant Lock是Java多线程编程中一种强大、灵活、自由度高的锁机制,其能够保证同步控制的正确性和可靠性,被广泛应用于实际开发中。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程并发编程(互斥锁Reentrant Lock) - Python技术站