首页
学习
活动
专区
圈层
工具
发布
20 篇文章
1
Java多线程编程-(20)-借ThreadLocal出现OOM内存溢出问题再谈弱引用WeakReference
2
Java多线程编程-(19)-多线程异步调用之Future模式
3
Java多线程编程-(18)-等待/通知模式接口Condition接口深入分析
4
Java多线程编程-(17)-读写锁ReentrantReadWriteLock深入分析
5
Java多线程编程-(16)-无锁CAS操作以及Java中Atomic并发包的“18罗汉”
6
Java多线程编程-(15)- 关于锁优化的几点建议
7
Java多线程编程-(14)-Java中的队列同步器AQS和ReentrantLock锁原理简要分析
8
Java多线程编程-(13)-从volatile和synchronized的底层实现原理看Java虚拟机对锁优化所做的努力
9
Java多线程编程-(12)-单例模式几种写法的错与对
10
Java多线程编程-(11)-面试常客ThreadLocal出现OOM内存溢出的场景和原理分析
11
Java多线程编程-(10)-看了这篇关于ThreadLocal的原理应该透彻了
12
Java多线程编程-(9)-使用线程池实现线程的复用和一些坑的避免
13
Java多线程编程-(8)-两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier
14
Java多线程编程-(7)-使用ReentrantReadWriteLock实现Lock并发
15
Java多线程编程-(6)-你还在使用wait/notify实现进程间的通信吗?
16
Java多线程编程-(5)-线程间通信机制的介绍与使用(温馨提示:图文较多,建议Wiff下打开)
17
Java多线程编程-(4)-线程本地ThreadLocal的介绍与使用
18
Java多线程编程-(3)-从一个错误的双重校验锁代码谈一下volatile关键字
19
Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性
20
Java多线程编程-(1)-线程安全和锁Synchronized概念

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()方法:判断是不是公平锁;

其他的不在介绍。

下一篇
举报
领券