ReentrantLock

ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock基本用法

先来看一下ReentrantLock的基本用法:

public class ThreadDomain38
{
private Lock lock = new ReentrantLock(); public void testMethod()
{
try
{
lock.lock();
for (int i = 0; i < 2; i++)
{
System.out.println("ThreadName = " + Thread.currentThread().getName() +
", i = " + i);
}
}
finally
{
lock.unlock();
}
}
}
public class MyThread38 extends Thread
{
private ThreadDomain38 td; public MyThread38(ThreadDomain38 td)
{
this.td = td;
} public void run()
{
td.testMethod();
}
}
public static void main(String[] args)
{
ThreadDomain38 td = new ThreadDomain38();
MyThread38 mt0 = new MyThread38(td);
MyThread38 mt1 = new MyThread38(td);
MyThread38 mt2 = new MyThread38(td);
mt0.start();
mt1.start();
mt2.start();
}

看一下运行结果:

ThreadName = Thread-1, i  = 0
ThreadName = Thread-1, i = 1
ThreadName = Thread-0, i = 0
ThreadName = Thread-0, i = 1
ThreadName = Thread-2, i = 0
ThreadName = Thread-2, i = 1

没有任何的交替,数据都是分组打印的,说明了一个线程打印完毕之后下一个线程才可以获得锁去打印数据,这也证明了ReentrantLock具有加锁的功能

ReentrantLock持有的是对象监视器

前面已经证明了ReentrantLock具有加锁功能,但我们还不知道ReentrantLock持有的是什么锁,因此写个例子看一下:

public class ThreadDomain39
{
private Lock lock = new ReentrantLock(); public void methodA()
{
try
{
lock.lock();
System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
} } public void methodB()
{
lock.lock();
System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
lock.unlock();
}
}

写两个线程分别调用methodA()和methodB()方法:

public class MyThread39_0 extends Thread
{
private ThreadDomain39 td; public MyThread39_0(ThreadDomain39 td)
{
this.td = td;
} public void run()
{
td.methodA();
}
}
public class MyThread39_1 extends Thread
{
private ThreadDomain39 td; public MyThread39_1(ThreadDomain39 td)
{
this.td = td;
} public void run()
{
td.methodB();
}
}

写一个main函数启动这两个线程:

public static void main(String[] args)
{
ThreadDomain39 td = new ThreadDomain39();
MyThread39_0 mt0 = new MyThread39_0(td);
MyThread39_1 mt1 = new MyThread39_1(td);
mt0.start();
mt1.start();
}

看一下运行结果:

MethodB begin ThreadName = Thread-1
MethodB begin ThreadName = Thread-1
MethodA begin ThreadName = Thread-0
MethodA end ThreadName = Thread-0

看不见时间,不过第四确实是格了5秒左右才打印出来的。从结果来看,已经证明了ReentrantLock持有的是对象监视器,可以写一段代码进一步证明这一结论,即去掉methodB()内部和锁相关的代码,只留下两句打印语句:

MethodA begin ThreadName = Thread-0
MethodB begin ThreadName = Thread-1
MethodB begin ThreadName = Thread-1
MethodA end ThreadName = Thread-0

看到交替打印了,进一步证明了ReentrantLock持有的是"对象监视器"的结论。

不过注意一点,ReentrantLock虽然持有对象监视器,但是和synchronized持有的对象监视器不是一个意思,虽然我也不清楚两个持有的对象监视器有什么区别,不过把methodB()方法用synchronized修饰,methodA()不变,两个方法还是异步运行的,所以就记一个结论吧----ReentrantLock和synchronized持有的对象监视器不同

另外,千万别忘了,ReentrantLock持有的锁是需要手动去unlock()的

Condition

synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:

1、一个Lock里面可以创建多个Condition实例,实现多路通知

2、notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的

看一下利用Condition实现等待/通知模型的最简单用法,下面的代码注意一下,await()和signal()之前,必须要先lock()获得锁,使用完毕在finally中unlock()释放锁,这和wait()/notify()/notifyAll()使用前必须先获得对象锁是一样的:

public class ThreadDomain40
{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); public void await()
{
try
{
lock.lock();
System.out.println("await时间为:" + System.currentTimeMillis());
condition.await();
System.out.println("await等待结束");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
} public void signal()
{
try
{
lock.lock();
System.out.println("signal时间为:" + System.currentTimeMillis());
condition.signal();
}
finally
{
lock.unlock();
}
}
}
public class MyThread40 extends Thread
{
private ThreadDomain40 td; public MyThread40(ThreadDomain40 td)
{
this.td = td;
} public void run()
{
td.await();
}
}
public static void main(String[] args) throws Exception
{
ThreadDomain40 td = new ThreadDomain40();
MyThread40 mt = new MyThread40(td);
mt.start();
Thread.sleep(3000);
td.signal();
}

看一下运行结果:

await时间为:1443970329524
signal时间为:1443970332524
await等待结束

差值是3000毫秒也就是3秒,符合代码预期,成功利用ReentrantLock的Condition实现了等待/通知模型。其实这个例子还证明了一点,Condition的await()方法是释放锁的,原因也很简单,要是await()方法不释放锁,那么signal()方法又怎么能调用到Condition的signal()方法呢?

注意要是用一个Condition的话,那么多个线程被该Condition给await()后,调用Condition的signalAll()方法唤醒的是所有的线程。如果想单独唤醒部分线程该怎么办呢?new出多个Condition就可以了,这样也有助于提升程序运行的效率。使用多个Condition的场景是很常见的,像ArrayBlockingQueue里就有。

Java多线程11:ReentrantLock的使用和Condition的更多相关文章

  1. Java多线程系列--“JUC锁”06之 Condition条件

    概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...

  2. java中使用ReentrantLock锁中的Condition实现三个线程之间通信,交替输出信息

    本文直接附上源代码,如下是自己写的一个例子 面试题需求: 使用Condition来实现 三个线程 线程1 线程2 线程3 三个交替输出 [按照 线程1(main)-->线程2-->线程3] ...

  3. java 多线程11:volatile关键字

    直接先举一个例子普通的线程实例变量的非可见性: public class MyThread28 extends Thread { private boolean isRunning = true; p ...

  4. Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

    概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...

  5. Java多线程——Condition条件

    简介 Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signa ...

  6. Java多线程系列--“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...

  7. Java多线程系列目录(共43篇)

    最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...

  8. java多线程系类:JUC锁:01之框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--"JUC锁"01之 框架02. Java多线程系列--"JUC锁&q ...

  9. Java多线程系列

    一.参考文献 1.:Java多线程系列目录 (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式 03. ...

随机推荐

  1. 字符串对象-String

    新建字符串对象 ① 直接赋值 ② 构造函数 ③ 转换函数 1    length              字符串对象属性 2    match()  null     跟php中的preg_matc ...

  2. NLS_LANG

    NLS_LANG是一个环境变量,用于定义语言,地域以及字符集属性.对于非英语的字符集,NLS_LANG的设置就非常重要. NLS:‘National Language Support (NLS)’ 当 ...

  3. SQL查询符合条件的记录的总数

    1. select count(*) from table;    //统计元组个数 2. select count(列名) from table;  //统计一列中值的个数 3. select co ...

  4. easyui datagrid加载json

      服务端: string pseries = context.Request["ajaxSearch"].ToString().Trim(); var jsonMap = new ...

  5. php和syslog

    syslog是Linux系统默认的日志守护进程.使用syslog可以方便把指定的事件写入特定文件中,可以让任何事件都登录到一台或多台服务器上. 1.简单例子,先说一下syslog怎么使用,以php为例 ...

  6. pd name与comment互换,或者code互换,总之互换

    1 PowerDesigner中批量根据对象的name生成comment的脚本 执行方法:Open PDM -- Tools -- Execute Commands -- Run Script Opt ...

  7. 查找原始MySQL死锁ID

    转载地址:http://yueliangdao0608.blog.51cto.com/397025/1180917 如果遇到死锁了,怎么解决呢?找到原始的锁ID,然后KILL掉一直持有的那个线程就可以 ...

  8. Ubuntu grub引导修复

    通过USB启动盘安装系统时将引导程序指定到/dev/sdb1,正常应该是指定到/dev/sdb才是,导致安装之后启动不起来. 重新通过USB启动盘进入试用界面,然后打开终端通过如下操作进行grub引导 ...

  9. UVALive 2191 Potentiometers (树状数组)

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  10. js多种切换图片

    分享通过js实现多种图片切换特效,这里只有手动切换哦,自动效果需自写,效果地址:http://dwz.cn/1drD5u. 下载地址:http://***/download/index/52209 适 ...