文章目录
一、ReentrantLock的底层实现
我们先看看ReentrantLock类中lock()
方法的实现
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
// 这里的compareAndSetState使用的就是CAS即自旋锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
我们再点进compareAndSetState()
方法
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
// 这里可以看到其实使用了Unsafe类
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,但同时也带来了指针的问题。Unsafe可以为JAVA提供依赖于硬件的CAS(compareAndSwap)原子操作。
二、ReentrantLock的基本用法
能够用synchronized关键字实现的地方,ReentrantLock也可以实现。
我们首先看一下下面两段功能完全相同的代码,一个是基于synchronized关键字实现,一个则基于ReentrantLock类实现。
基于synchronized关键字实现的代码:
/**
* @author IT00ZYQ
* @Date 2021/3/4 10:29
**/
public class T01_ReentrantLock {
synchronized void m1() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1 running ...");
}
}
synchronized void m2() {
System.out.println("m2 running ...");
}
public static void main(String[] args) {
T01_ReentrantLock t = new T01_ReentrantLock();
// m1方法加了synchronized锁,当前对象已被锁住
new Thread(t::m1).start();
// 这里睡一秒是为了保证让线程new Thread(t::m1)先执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// m2方法需要获取当前对象锁,必须等待上面的线程运行结束释放锁
new Thread(t::m2).start();
}
}
基于ReentrantLock类实现的代码:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author IT00ZYQ
* @Date 2021/3/4 10:29
**/
public class T02_ReentrantLock {
Lock lock = new ReentrantLock();
void m1() {
lock.lock(); // 相当于synchronize(this)
try {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1 running ...");
}
} finally {
lock.unlock();
}
}
void m2() {
lock.lock(); // 相当于synchronize(this)
try {
System.out.println("m2 running ...");
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
T02_ReentrantLock t = new T02_ReentrantLock();
// m1方法加了调用了lock(),当前对象已被锁住
new Thread(t::m1).start();
// 这里睡一秒是为了保证让线程new Thread(t::m1)先执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// m2方法需要调用lock(),必须等待上面的线程运行结束释放锁
new Thread(t::m2).start();
}
}
三、ReentrantLock的特点
3.1、ReentrantLock是可重入锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author IT00ZYQ
* @date 2021/5/23 21:31
**/
public class T02_ReentrantLock_02 {
public final Lock lock = new ReentrantLock();
public void m() {
lock.lock();
try {
for (int i = 1; i <= 10; i++) {
System.out.println("m() 方法中循环进行了" + i + "次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 由于mm()方法中也需要lock锁
// 只有ReentrantLock是可重入锁
// 这里的mm()方法才能成功调用
if (i == 3) {
mm();
}
}
} finally {
lock.unlock();
}
}
public void mm() {
lock.lock();
try {
System.out.println("mm() 方法被调用了");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
T02_ReentrantLock_02 t = new T02_ReentrantLock_02();
Thread thread = new Thread(t::m);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
m() 方法中循环进行了1次
m() 方法中循环进行了2次
m() 方法中循环进行了3次
mm() 方法被调用了
m() 方法中循环进行了4次
m() 方法中循环进行了5次
m() 方法中循环进行了6次
m() 方法中循环进行了7次
m() 方法中循环进行了8次
m() 方法中循环进行了9次
m() 方法中循环进行了10次
Process finished with exit code 0
3.2、ReentrantLock的公平属性
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author IT00ZYQ
* @date 2021/5/23 21:45
**/
public class T02_ReentrantLock_03 {
/**
* 默认是非公平锁,传入true是为公平锁
*/
public final Lock lock = new ReentrantLock(true);
public void m() {
for (int i = 1; i <= 20; i++) {
lock.lock();
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
T02_ReentrantLock_03 t = new T02_ReentrantLock_03();
Thread thread1 = new Thread(t::m, "线程1");
Thread thread2 = new Thread(t::m, "线程2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
设置为公平锁时,运行结果大部分是交替输出的,只复制了一部分:
线程1
线程2
线程1
线程2
线程1
线程2
线程1
线程2
线程1
设置为非公平锁时,会出现某一线程连续输出多次的结果,只复制了一部分:
线程1
线程1
线程1
线程2
线程2
线程2
线程2
线程2
3.3、ReentrantLock中的Condition
每个一个Condition相当于一个等待队列,ReentrantLock可以创建多个不同的等待队列,这样的话,我们可以根据需要唤醒处于WAIT状态的某一类线程。如生产者与消费者问题中,生产者线程生产完成后,应该只负责唤醒消费者线程进行消费,而无需唤醒其他的生产者线程,详细内容可阅读文章生产者消费者问题。