J.U.C核心之AQS(ReentrantLock)
J.U.C:Java.util.concurrent工具包
AQS: AbstractQueuedSynchronizer
一个持有锁的线程,在释放锁之前,如果再次访问加了该同步锁的其他方法, 这个线程不需要再次争抢锁,只需要记录重入次数
重入锁可以解决死锁的问题。
一、ReentrantLock实现原理
重入锁 -> 互斥锁
与Synchronized区别:需要手动加锁解锁。
二、锁的设计思想(如果我们自己去实现)
- 一定会设计到锁的抢占,需要一个标记来实现互斥。全局变量(0,1)
- 抢到了锁怎么处理
- 没抢到锁怎么处理
- 需要等待(让处于排队中的线程,如果没有抢占到锁,则先阻塞->释放cpu资源)
- 如何让线程等待?
- wait/notify(线程通信的机制,无法指定唤醒某一个线程)
- LockSupport.park/unpark(阻塞或唤醒一个指定的线程)
- Condition(条件)
- 如何让线程等待?
- 需要排队(允许有N个线程被阻塞,此时线程处于活跃状态)
- 通过一个数据结构,把这N个排队的线程存储起来
- 需要等待(让处于排队中的线程,如果没有抢占到锁,则先阻塞->释放cpu资源)
- 抢到锁的释放过程,如何处理
- LockSupport.park/unpark -> 唤醒处于队列中的指定线程
- 锁抢占的公平性
- 公平
- 非公平(性能会更高)
public class LockDemo {
static Lock lock=new ReentrantLock(); //重入锁
public static int count=0;
public static void incr(){ //递增
try {
lock.lock(); //手动获得锁 线程A
Thread.sleep(1);
decr();
count++; //count++ (只会由一个线程来执行)
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock(); //一定要在finall中释放锁
}
}
public static void decr(){ //递减
lock.lock(); //有需要争抢锁 ,线程A (不需要争抢锁,记录重入次数即可)
count--;
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(LockDemo::incr).start();
}
Thread.sleep(4000);
System.out.println("result:"+count);
}
}
三、AQS
RenentrantLock的类关系图
思考:锁的位置如何实现线程的阻塞和唤醒的?
static Lock lock = new ReentrantLock(); //重入锁
lock.lock();
多个线程中线程的阻塞、获得锁的资格都是在lock()方法中
四、线程的通信
共享内存
wait和notify -> 基于某一个条件来等待或者唤醒
线程A:wait方法调用的时候,一定会让线程A等待并释放锁。
public class Producer implements Runnable{
//加锁对象,两个线程都是基于这个队列进行通信的
private Queue<String> bags;
private int size;
public Producer(Queue<String> bags, int size) {
this.bags = bags;
this.size = size;
}
@Override
public void run() {
int i=0;
while(true){
i++;
synchronized (bags){
while(bags.size()==size){
System.out.println("bags已经满了");
//TODO? 阻塞?
try {
bags.wait();// 阻塞当前线程并释放producer抢到的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者-生产:bag"+i);
bags.add("bag"+i);
//TODO? 唤醒处于阻塞状态下的消费者
bags.notifyAll();
}
}
}
}
public class Consumer implements Runnable{
private Queue<String> bags;
private int size;
public Consumer(Queue<String> bags, int size) {
this.bags = bags;
this.size = size;
}
@Override
public void run() {
while(true){
synchronized (bags){
while(bags.isEmpty()){
System.out.println("bags为空");
try {
bags.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String bag=bags.remove();
System.out.println("消费者消费:"+bag);
bags.notifyAll();// 这里只是唤醒Producer线程,但是Producer线程并不能马上执行
}// 线程能够执行条件:同步代码块结束
}
}
}
调用:
public class WaitNotifyDemo {
public static void main(String[] args) {
Queue<String> queue=new LinkedList<>();
int size=10;
Producer producer=new Producer(queue,size);
Consumer consumer=new Consumer(queue,size);
Thread t1=new Thread(producer);
// 要保证Producer先执行。notify方法不会释放锁
Thread t2=new Thread(consumer);
t1.start();
t2.start();
}
}
五、Condition条件
等价于wait/notify -> J.U.C 包中实现的wait和notify
- 在J.U.C中锁的实现是lock
- wait/notify,锁的实现是synchronized
Condition是一个多线程协调通信的工具类,可以让某些线程一起等待 某个条件(condition),只有满足条件时,线程才会被唤醒
作用:实现线程的阻塞和唤醒
前提条件:必须先要获得锁
await/signal:signalAll
- await -> 让线程阻塞,并且释放锁
- signal -> 唤醒阻塞的线程,并且释放锁
// 获取到锁之后阻塞lock.lock ->> condition.await
public void ConditionDemoWait implements Runnable{
private Lock lock;
private Condition condition;
public ConditionDemoWait (Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin - ConditionDemoWait");
lock.lock();
try{
condition.await();// 一定要在锁的范围内:即locl.lock之后。让当前线程阻塞
System.out.println("end - ConditionDemoWait");
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public void ConditionDemoNotify implements Runnable{
private Lock lock;
private Condition condition;
public ConditionDemoNotify (Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin - ConditionDemoNotify");
lock.lock();
try{
condition.signal(); // 唤醒处于等待下的线程
System.out.println("end - ConditionDemoNotify");
}catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 同一把锁并在同一个条件下
ConditionDemoWait cd = new ConditionDemoWait(lock, condition);
ConditionDemoNotify cdn = new ConditionDemoNotify(lock, condition);
new Thread(cd).start();
new Thread(cdn).start();
}
结果:
begin - ConditionDemoWait
begin - ConditionDemoNotify
end - ConditionDemoNotify
end - ConditionDemoWait
(lock, condition);
ConditionDemoNotify cdn = new ConditionDemoNotify(lock, condition);
new Thread(cd).start();
new Thread(cdn).start();
}
结果:
> begin - ConditionDemoWait
>
> begin - ConditionDemoNotify
>
> end - ConditionDemoNotify
>
> end - ConditionDemoWait