本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
synchronized与ReentrantLock
- 首先去根上回答的话就是
- 因为
synchronized是有JDK提供的锁- 底层涉及到指令集的层面,如:
-
同步代码块是利用
monitorenter和monitorexit指令实现的 -
同步方法则是利用 flags 实现的。
- 因为
- 而
RenntranLock是java5提供的锁实现-
是
java.util.concurrent包下提供的一套互斥锁 -
但是和
synchronized相比,其具有很多synchronized不能实现的功能 -
比如最明显的例子就是
RenntranLock可以实现「公平性」的自定义
-
线程安全的概念:保证在多线程环境下的共享、可修改的状态是正确的
- 来看下面代码
public class test {
public int state;
public void testSafe() {
while (state < 100000) {
int i = state++;
int j = state;
if (i != j - 1) {
System.out.println("i is: " + i + "; j is: " + j);
}
}
}
public static void main(String[] args) throws InterruptedException {
test t = new test();
Thread ta = new Thread() {
@Override
public void run() {
t.testSafe();
}
};
Thread tb = new Thread() {
@Override
public void run() {
t.testSafe();
}
};
ta.start();
tb.start();
ta.join();
tb.join();
}
}
- 下图为代码的输出
- 很明显看到出现了并发的线程修改了
state的值
- 很明显看到出现了并发的线程修改了
- 针对以上代码如果加上
synchronized锁🔐的话,就不会有值的改变情况
synchronized (this){
while (state < 100000) {
int i = state++;
int j = state;
if (i != j - 1) {
System.out.println("i is: " + i + "; j is: " + j);
}
}
}
-
上面代码我们使用的是
synchronized(class){}锁住的是代码块- 而使用
RenntranLock的话是代表一个线程试图获取一个已经获取锁的时候- 那么这个动作就是表示自动成功了
- 公平性是指更偏向于把锁🔐赋予给等待时间最久的线程
- 上部操作是为的是减少线程的饥饿
- 而使用
-
ReentrantLock相比synchronized,可以像普通对象一样使用- 可以利用其提供的各种便利方法,进行精细的同步操作,甚至是实现synchronized难以表达的用例,如:
-
带超时的获取锁尝试。
-
可以判断是否有线程,或者某个特定线程,在排队等待获取锁。
-
可以响应中断请求。
-
与重入锁相关的队列
- 可以利用其提供的各种便利方法,进行精细的同步操作,甚至是实现synchronized难以表达的用例,如:
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
- 在
ArrayBlockingQueue源码中我们可以看到- 两个条件变量是从同一个
ReentrantLock创建出来的
- 两个条件变量是从同一个
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
- 当队列为空的时候
take的线程调用await()执行等待入队- 不是直接返回,这就是形成了
BlockingQueue的意思 - 这一步是重点在
notEmpty身上 入队操作
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
- 入队操作住傲视通过
signal()完成通知线程- 所以通过
signal()和await()的组合就完成了状态的闭环
- 所以通过