专栏首页Java后端技术栈Java多线程编程-(6)-你还在使用wait/notify实现进程间的通信吗?

Java多线程编程-(6)-你还在使用wait/notify实现进程间的通信吗?

在《Java多线程编程-(5)-线程间通信机制的介绍与使用》已经学习了,可以使用方法wait/notify 结合同步关键字synchronized实现同步和线程间通信,下边介绍一种更为方便的方式实现同步和线程间通信的效果,那就是Lock对象。

Lock对象简介

这里为什么说Lock对象哪?Lock其实是一个接口,在JDK1.5以后开始提供,其实现类常用的有ReentrantLock,这里所说的Lock对象即是只Lock接口的实现类,为了方便记忆或理解,都简称为Lock对象。

我们知道synchronized关键字可以实现线程间的同步互斥,从JDK1.5开始新增的ReentrantLock类能够达到同样的效果,并且在此基础上还扩展了很多实用的功能,比使用synchronized更佳的灵活。

下边,就开始一起学习一下ReentrantLock对象。

ReentrantLock实现线程同步

运行结果:

可以看出,当前线程打印完毕之后释放锁,其他线程才可以获取锁然后进行打印。线程打印的数据是分组打印的,这是因为当前线程已经持有锁,在当前线程打印完之后才会释放锁,但线程之间打印的顺序是随机的。

为了进一步说明使用ReentrantLock可以实现线程之间同步,测试代码如下:

运行结果:

可以看出,在sleep指定的时间内,当调用了lock.lock()方法线程就持有了”对象监视器”,其他线程只能等待锁被释放后再次争抢,效果和使用synchronized关键字是一样的。

使用Lock对象实现线程间通信

上述,已经大致看了一下如何使用ReentrantLock实现线程之间的同步,下边再看一下ReentrantLock是如何实现线程间通信的。

在前文中我们已经知道可以使用关键字synchronized与wait()方法和notify()方式结合实现线程间通信,也就是等待/通知模式。在ReentrantLock中,是借助Condition对象进行实现的。

Condition的创建方式如下:

Condition按字面意思理解就是条件,当然,我们也可以将其认为是条件进行使用,这样的话我们可以通过上述的代码创建多个Condition条件,我们就可以根据不同的条件来控制现成的等待和通知。而我们还知道,在使用关键字synchronized与wait()方法和notify()方式结合实现线程间通信的时候,notify/notifyAll的通知等待的线程时是随机的,显然使用Condition相对灵活很多,可以实现”选择性通知”。

这是因为,synchronized关键字相当于整个Lock对象只有一个单一的Condition对象,所有的线程都注册到这个对象上。线程开始notifAll的时候,需要通知所有等待的线程,让他们开始竞争获得锁对象,没有选择权,这种方式相对于Condition条件的方式在效率上肯定Condition较高一些。

下边,我们首先看一个实例。

使用Lock对象和Condition实现等待/通知实例

主要方法对比如下:

(1)Object的wait()方法相当于Condition类中的await()方法; (2)Object的notify()方法相当于Condition类中的signal()方法; (3)Object的notifyAll()方法相当于Condition类中的signalAll()方法;

首先,使用Lock的时候,和《Java多线程编程-(4)-线程间通信机制的介绍与使用》介绍的一样,都需要先获取锁。

示例代码如下:

运行结果:

可以看出结果正确执行!

使用Lock对象和多个Condition实现等待/通知实例

示例代码如下:

运行结果:

可以看出实现了分别通知。因此,我们可以使用Condition进行分组,可以单独的通知某一个分组,另外还可以使用signalAll()方法实现通知某一个分组的所有等待的线程。

公平锁和非公平锁

概念很好理解,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配,即先进先出,那么他就是公平的;非公平是一种抢占机制,是随机获得锁,并不是先来的一定能先得到锁,结果就是不公平的。

ReentrantLock提供了一个构造方法,可以很简单的实现公平锁或非公平锁,源代码构造函数如下:

参数:fair为true表示是公平锁,反之为非公平锁,这里不再写代码测试。

ReentrantLock的其他方法

ReentrantLock源代码结构如下:

方法很简单,看到名称就可以想到作用是什么,挑一些简单介绍一下:

(1)getHoldCount()方法:查询当前线程保持此锁定的个数,也就是调用lock()的次数;

(2)getQueueLength()方法:返回正等待获取此锁定的线程估计数目;

(3)isFair()方法:判断是不是公平锁;

其他的不在介绍。

本文分享自微信公众号 - Java后端技术(JavaITWork),作者:徐刘根

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-10-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java多线程编程-(9)-使用线程池实现线程的复用和一些坑的避免

    假设这里有一个系统,大概每秒需要处理5万条数据,这5万条数据为一个批次,而这没秒发送的5万条数据数据需要经过两个处理过程,第一步是数据存入数据库,第二步是对数据...

    Java后端技术
  • Java多线程编程-(5)-线程间通信机制的介绍与使用(温馨提示:图文较多,建议Wiff下打开)

    我们知道线程是操作系统中独立的个体,但是这个单独的个体之间没有一种特殊的处理方式使之成为一个整体,线程之间没有任何交流和沟通的话,他就是一个个单独的个体,不足以...

    Java后端技术
  • Java多线程编程-(13)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力

    对于Java来说我们知道,Java代码首先会编译成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上进行执行。

    Java后端技术
  • 详解 Tomcat 的连接数与线程池

    前言 在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。 在前面的文章 详...

    Java高级架构
  • 干货|Tomcat 连接数与线程池详解前言

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    美的让人心动
  • 一台 Java 服务器可以跑多少个线程?

    打出jstack文件,通过IBM Thread and Monitor Dump Analyzer for Java工具查看如下:

    Java技术栈
  • 栈和堆的访问速度以及对象创建

     首先回答是不是的问题:对象并非只能存在于内存中的堆,其可以存于栈上。这是因为栈和堆在内存角度上看,没有任何区别。

    Fisherman渔夫
  • 初学者第66节生产者消费者(八)

    上一节讲解了生产者与消费者模式的基本理论以及简单实现,并且遗留下来一个消费商品是null,库存为:-1的问题 ,看下代码。

    用户5224393
  • 详解tomcat的连接数与线程池

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。

    小柒2012
  • 线程安全(上)--彻底搞懂synchronized(从偏向锁到重量级锁)

    接触过线程安全的同学想必都使用过synchronized这个关键字,在java同步代码快中,synchronized的使用方式无非有两个:

    帅地

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动