ReenTrantLock可重入锁(和synchronized的区别)总结
可重入性:
从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大。两者都是同一个线程没进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
锁的实现:
Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的,有什么区别,说白了就类似于操作系统来控制实现和用户自己敲代码实现的区别。前者的实现是比较难见到的,后者有直接的源码可供阅读。
性能的区别:
在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
功能区别:
便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized
ReenTrantLock独有的能力:
1. ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
2. ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
3. ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
ReenTrantLock实现的原理:
在网上看到相关的源码分析,本来这块应该是本文的核心,但是感觉比较复杂就不一一详解了,简单来说,ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。它的性能比较好也是因为避免了使线程进入内核态的阻塞状态。想尽办法避免线程进入内核的阻塞状态是我们去分析和理解锁设计的关键钥匙。
什么情况下使用ReenTrantLock:
答案是,如果你需要实现ReenTrantLock的三个独有功能时。
sychronized
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
sychronized有三条原则:
1. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
2. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
3. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
synchronized会在进入同步块的前后分别形成monitorenter和monitorexit字节码指令.在执行monitorenter指令时会尝试获取对象的锁,如果此没对象没有被锁,或者此对象已经被当前线程锁住,那么锁的计数器加一,每当monitorexit被锁的对象的计数器减一.直到为0就释放该对象的锁.由此synchronized是可重入的,不会出现自己把自己锁死.
ReentrantLock
ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”。
顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。
ReentrantLock分为 公平锁 和 非公平锁 。它们的区别体现在获取锁的机制上是否公平。锁 是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在同一个时间点只能被一个线程获取(当某线程获取到“锁”时,其它线程就必须等待);ReentraantLock是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
以对象的方式来操作对象锁,相对于sychronized需要在finally中去释放锁。
synchronized和ReentrantLock的区别
除了synchronized的功能,多了三个高级功能。
等待可中断,公平锁,绑定多个Condition。
1. 等待可中断:在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待,tryLock(long timeout, TimeUnit unit)
2. 公平锁:按照申请锁的顺序来一次获得锁称为公平锁,synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁。new RenentrantLock(boolean fair)
3. 绑定多个Condition:通过多次newCondition可以获得多个Condition对象,可以简单的实现比较负责的线程同步的功能,通过await(),signal();
实现消费者模式
sychronized
public static class Depot {
private int mCapacity;
private int mSize;
public Depot(int capacity) {
mCapacity = capacity;
mSize = 0;
}
public synchronized void produce(int val) {
try {
int left = val;
while (left > 0) {
while (mSize >= mCapacity)
wait();
int inc = (mSize + left) > mCapacity ? (mCapacity - mSize) : left;
mSize += inc;
left -= inc;
System.out.println("produce Thread: " + Thread.currentThread().getName() + " val: " + val + " mSize: " + mSize + " left: " + left);
notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void consume(int val) {
try {
int left = val;
while (left > 0) {
while (mSize <= 0)
wait();
int inc = (mSize - left) < 0 ? mSize : left;
mSize -= inc;
left -= inc;
System.out.println("consume Thread: " + Thread.currentThread().getName() + " val: " + val + " mSize: " + mSize + " left: " + left);
notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class Producer {
private Depot depot;
public Producer(Depot depot) {
this.depot = depot;
}
public void produce(int val) {
new Thread() {
@Override
public void run() {
depot.produce(val);
}
}.start();
}
}
static class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
public void consume(int val) {
new Thread() {
@Override
public void run() {
depot.consume(val);
}
}.start();
}
}
public static void main(String[] args) {
Depot depot = new Depot(100);
Producer producer = new Producer(depot);
Customer customer = new Customer(depot);
producer.produce(60);
producer.produce(120);
customer.consume(90);
customer.consume(150);
producer.produce(110);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
ReentrantLock
public static class Depot {
private int mCapacity;
private int mSize;
private Lock mLock;
private Condition mFullCondition;
private Condition mEmptyCondition;
public Depot(int capacity) {
mCapacity = capacity;
mSize = 0;
mLock = new ReentrantLock();
mFullCondition = mLock.newCondition();
mEmptyCondition = mLock.newCondition();
}
public void produce(int val) {
mLock.lock();
try {
int left = val;
while (left > 0) {
while (mSize >= mCapacity)
mFullCondition.await();
int inc = (mSize + left) > mCapacity ? (mCapacity - mSize) : left;
mSize += inc;
left -= inc;
System.out.println("produce Thread: " + Thread.currentThread().getName() + " mSize: " + mSize + " left: " + left);
mEmptyCondition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
}
public void consume(int val) {
mLock.lock();
try {
int left = val;
while (left > 0) {
while (mSize <= 0)
mEmptyCondition.await();
int inc = (mSize - left) < 0 ? mSize : left;
mSize -= inc;
left -= inc;
System.out.println("consume Thread: " + Thread.currentThread().getName() + " mSize: " + mSize + " left: " + left);
mFullCondition.signal();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mLock.unlock();
}
}
}
static class Producer {
private Depot depot;
public Producer(Depot depot) {
this.depot = depot;
}
public void produce(int val) {
new Thread() {
@Override
public void run() {
depot.produce(val);
}
}.start();
}
}
static class Customer {
private Depot depot;
public Customer(Depot depot) {
this.depot = depot;
}
public void consume(int val) {
new Thread() {
@Override
public void run() {
depot.consume(val);
}
}.start();
}
}
public static void main(String[] args) {
Depot depot = new Depot(100);
Producer producer = new Producer(depot);
Customer customer = new Customer(depot);
producer.produce(60);
producer.produce(120);
customer.consume(90);
customer.consume(150);
producer.produce(110);
}