Java~多线程ReentrantLock的公平锁和非公平锁 ,结合Condition实现部分路通知和选择路通知

public class Demo2 {

public static void main(String[] args) throws InterruptedException {

//传入true表示公平, 传入false表示不公平

Service2 service2 = new Service2(true);

Runnable runnable = new Runnable() {

@Override

public void run() {

service2.method();

}

};

//创建多个线程

Thread[] threads = new Thread[10];

for (int i = 0; i < 10; i++) {

threads[i] = new Thread(runnable);

}

for (int i = 0; i < 10; i++) {

threads[i].start();

}

for (int i = 0; i < 10; i++) {

threads[i].join();

}

}

}

  • 当我们传入的是true表示公平锁 其运行结果为

在这里插入图片描述

  • 当我们传入false表示不公平时运行结果为

在这里插入图片描述

  • 从结果上就可以看出差距, 公平锁上锁的顺序是有序的, 还要注意ReentrantLock默认的是非公平锁

**

  • 通过看源码可以发现, 如果是公平锁, 在lock的时候会走fairSync,

  • 然后判断此时队列中有没有等待的线程, 如果有就加入队列尾部保证公平性

  •     if (!tryAcquire(arg) &&
    
  •         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    
  •         selfInterrupt();
    
  • }
    
  • 如果不是公平的, 就会走NoFairSync, 直接CAS检查此时的state是不是为0, 如果此时正好为0就去执行了

  • 也就破坏了公平性

  •     final void lock() {
    
  •         if (compareAndSetState(0, 1))
    
  •             setExclusiveOwnerThread(Thread.currentThread());
    
  •         else
    
  •             acquire(1);
    
  •     }
    

Condition对象监视器


  • 关键字synchronized与wait()和notifyO/notifyAllO)方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition 对象。Condition 类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。在使用notify()/notifyAllO)方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。

  • 而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll)时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

Condition的实现分析

  • 首先Condition的操作需要在获取到相关锁的条件下, 所以他是作为同步器的内部类的

  • 每个Condition对象内部都有一个队列, 是一个等待队列, 该队列是Condition对象实现等待 通知的功能关键

  • 这个等待队列是一个FIFO先进先出的队列, 在队列的每一个节点上都是一个线程的引用, 该线程就是Condition对象上等待的线程, 如果一个线程执行了Condition.await() 就会释放锁然后进入这个队列中, 等待通知的时候, 也就是执行Condition.signal() 方法时, 将会唤醒这个Condition对象的等待队列中的等待时间最长的那个线程, 也就是头结点引用的那个线程, 这个线程就会进入到同步队列中, 并将这个线程再等待队列中删掉

  • 使用Condition和synchronized一样都要在加锁的情况下使用, 否则会报异常

演示Condition的等待与通知

  • Object类中的wait()方法相当于Condition类中的await()方法。

  • Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

  • Object类中的notify方法相当于Condition类中的signal(方法。

  • Object类中的notifyAll 方法相当于Condition类中的signalAll()方法。

  • 下面进行演示

  • 服务类

public class Service3 {

private ReentrantLock reentrantLock = new ReentrantLock();

private Condition condition = reentrantLock.newCondition();

public void methodAwait() {

System.out.println(Thread.currentThread().getName() + " 开始运行");

reentrantLock.lock();

System.out.println(Thread.currentThread().getName() + " 执行await");

try {

condition.await();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

reentrantLock.unlock();

System.out.println(Thread.currentThread().getName() + " 运行结束");

}

}

public void methodSignal() {

System.out.println(Thread.currentThread().getName() + " 开始运行");

reentrantLock.lock();

System.out.println(Thread.currentThread().getName() + " 执行signal");

try {

condition.signal();

} finally {

reentrantLock.unlock();

System.out.println(Thread.currentThread().getName() + " 运行结束");

}

}

}

  • 运行类

public class Demo3 {

public static void main(String[] args) throws InterruptedException {

Service3 service3 = new Service3();

Thread a = new Thread(“A”) {

@Override

public void run() {

service3.methodAwait();

}

};

Thread b = new Thread(“B”) {

@Override

public void run() {

service3.methodSignal();

}

};

a.start();

Thread.sleep(100);

b.start();

}

}

  • 运行结果

在这里插入图片描述

实现部分路通知和选择路通知

  • 服务类

public class Service4 {

private ReentrantLock reentrantLock = new ReentrantLock();

private Condition conditionA = reentrantLock.newCondition();

private Condition conditionB = reentrantLock.newCondition();

public void awaitA() {

reentrantLock.lock();

System.out.println(Thread.currentThread().getName() + " 开始运行");

System.out.println(Thread.currentThread().getName() + " 执行awaitA");

try {

conditionA.await();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

System.out.println(Thread.currentThread().getName() + " 运行结束");

reentrantLock.unlock();

}

}

public void awaitB() {

reentrantLock.lock();

System.out.println(Thread.currentThread().getName() + " 开始运行");

System.out.println(Thread.currentThread().getName() + " 执行awaitB");

try {

conditionB.await();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

System.out.println(Thread.currentThread().getName() + " 运行结束");

reentrantLock.unlock();

}

}

public void signalA() {

reentrantLock.lock();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

image

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
lt=“img” style=“zoom: 33%;” />

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

[外链图片转存中…(img-vDqnWRYw-1711981443250)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值