文章目录
一、Lock
Lock 是一个接口,主要实现类包括:
- ReentrantLock 可重入锁(常用)
- ReentrantReadWriteLock.ReadLock 读锁
- ReentrantReadWriteLock.WriteLock 写锁
二、ReentrantLock
ReentrantLock 分为公平锁和非公平锁,源码如下
公平锁:非常公平,先来后到
非公平锁:可以插队
Java 中默认是非公平锁
/**
* 非公平锁
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 传True 公平锁
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
用法
Lock lock = new ReentrantLock();
public void test () {
// 锁
lock.lock();
try {
// 你的业务代码,需要加锁的部分
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
三、 Lock 锁和 Synchronized 的区别
- Synchronized 是 Java 的内置关键字,Lock 是一个接口
- Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized 会自动释放锁,Lock 必须手动释放。不释放就会死锁
- Synchronized 线程一获取到锁,线程二只能等待;线程一如果阻塞了,线程二必须一直等下去。Lock锁可以使用 lock.tryLock() 方法,不一定要一直等下去
- Synchronized 不可以中断,是非公平锁。Lock锁可以判断锁,可以设置是公平还是非公平
- Synchronized 适合少量的同步代码,Lock适合大量的代码块
四、 传统 Synchronized 生产者和消费者问题
虚假唤醒
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功。
为什么用if判断会有虚假唤醒问题
例如一个生产者线程执行完之后,唤醒所有线程,被另外一个生产者线程拿到了锁,因为if判断只会执行一次,所以此时不会判断是否需要等待,直接就执行了if判断是否的代码,就导致了问题
public class Test {
public static void main(String[] args) {
Tiecket tiecket = new Tiecket();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.increase();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.decrease();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.increase();
}
}, "C").start();
}
}
class Tiecket {
private int number = 0;
public synchronized void increase() {
while (number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number ++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
this.notifyAll();;
}
public synchronized void decrease() {
while (number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number --;
System.out.println(Thread.currentThread().getName() + "=>" + number);
this.notifyAll();;
}
}
五、Lock 和 Condition 生产者消费者问题
其实就是把this.wait() 换成了 condition.await()
this.notifyAll() 换成 condition.signalAll()
public class Test {
public static void main(String[] args) {
Tiecket tiecket = new Tiecket();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.increase();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.decrease();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tiecket.increase();
}
}, "C").start();
}
}
class Tiecket {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increase() {
lock.lock();
try {
while (number != 0) {
condition.await();
}
number ++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrease() {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number --;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
六、Condition 精准唤醒
我们创建多个监视器condition,condition2.signal() 代表唤醒condition2,与notify()不同 notify()是随机唤醒一个线程
public class Test {
public static void main(String[] args) {
Order order = new Order();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
order.first();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
order.second();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
order.third();
}
}, "C").start();
}
}
class Order {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void first() {
lock.lock();
try {
while (number % 3 != 0) {
condition1.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void second() {
lock.lock();
try {
while (number % 3 != 1) {
condition2.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void third() {
lock.lock();
try {
while (number % 3 != 2) {
condition3.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}