(一)Java中线程协作的最常见的两种方式:

(1)利用Object的wait()、notify()和notifyAll()方法及synchronized

(2)使用Condition、ReentrantLock

(二)Object类的wait()、notify()和notifyAll()方法

 /**
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the wait methods
*/
public final native void notify(); /**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* wait methods.
*/
public final native void notifyAll(); /**
* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.
* <p>
* The current thread must own this object's monitor.
*/
public final native void wait(long timeout) throws InterruptedException;

从这三个方法的文字描述可以知道以下几点信息:

  1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

  2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

  3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

  4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

(三)Lock接口,ReentrantLock、Condition说明

3.1重要概念:可重入性

可重入性描述这样的一个问题:一个线程在持有一个锁的时候,它内部能否再次(多次)申请该锁。如果一个线程已经获得了锁,其内部还可以多次申请该锁成功。那么我们就称该锁为可重入锁。通过以下伪代码说明:

 void methodA(){
lock.lock(); // 获取锁
methodB();
lock.unlock() // 释放锁
} void methodB(){
lock.lock(); // 获取锁
// 其他业务
lock.unlock();// 释放锁
}

可重入锁可以理解为锁的一个标识。该标识具备计数器功能。标识的初始值为0,表示当前锁没有被任何线程持有。每次线程获得一个可重入锁的时候,该锁的计数器就被加1。每次一个线程释放该所的时候,该锁的计数器就减1。前提是:当前线程已经获得了该锁,是在线程的内部出现再次获取锁的场景

3.2 ReentrantLock实现说明
该demo模拟电影院的售票情况,tickets总票数。开启了10个窗口售票,售完为止

 public class ReentrantLockDemo01 implements Runnable {

     private Lock lock = new ReentrantLock();

     private int tickets = 200;

     @Override
public void run() {
while (true) {
lock.lock(); // 获取锁
try {
if (tickets > 0) {
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread().getName() + " " + tickets--);
} else {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放所
}
}
} public static void main(String[] args) {
ReentrantLockDemo01 reentrantLockDemo = new ReentrantLockDemo01();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(reentrantLockDemo, "thread" + i);
thread.start();
}
}
}

3.3lockInterruptibly()方法说明

从Lock的源码可以看出:lockInterruptibly() 抛出中断异常

 void lockInterruptibly() throws InterruptedException;

3.4 tryLock(),tryLock(long time, TimeUnit unit)方法说明

tryLock()方法立刻返回当前获取情况。

tryLock(long time, TimeUnit unit)等待一定的时间,返回获取情况

 public class ReentrantLockDemo03 implements Runnable {

     private ReentrantLock lock = new ReentrantLock();

     @Override
public void run() {
try {
if (lock.tryLock(2, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + " 获取当前lock锁");
TimeUnit.SECONDS.sleep(4);
} else {
System.out.println(Thread.currentThread().getName()+ " 获取锁失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} public static void main(String[] args) {
ReentrantLockDemo03 reentrantLockDemo = new ReentrantLockDemo03();
Thread thread01 = new Thread(reentrantLockDemo, "thread01");
Thread thread02 = new Thread(reentrantLockDemo, "thread02");
thread01.start();
thread02.start();
}

3.5 newCondition() 方法说明

目前只是对newCondition()使用方式进行说明,没有深入的分析Condition()的实现源码。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和”同步锁”(synchronized关键字)捆绑使用的;而Condition是需要与”互斥锁”/”共享锁”捆绑使用的。

3.6 Condition

Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,在阻塞队列那一篇博文中就讲述到了,阻塞队列实际上是使用了Condition来模拟线程间协作。

  • Condition是个接口,基本的方法就是await()和signal()方法;
  • Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
  • 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用

  Conditon中的await()对应Object的wait();

  Condition中的signal()对应Object的notify();

  Condition中的signalAll()对应Object的notifyAll()。

(四)两种方式的多线程通信的实现

 package cn.csrc.base.cpu;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
*
*功能说明:线程间通信的两种方式 (1)Object (2)ReentrantLock
*@author:zsq
*create date:2019年7月2日 下午4:23:41
*修改人 修改时间 修改描述
*Copyright
*/
public class OddEvenPrinter { //第一种方法 object作为锁
private final Object obj=new Object(); //第二种方法
private final ReentrantLock lock=new ReentrantLock();
private final Condition condition=lock.newCondition(); private int limit;
private volatile int count; public OddEvenPrinter(int limit,int count){
this.limit=limit;
this.count=count;
} //Object锁
public void myPrint1(){
synchronized (obj) {
while(count<limit){
try {
System.out.println(String.format("线程[%s]打印数字:%d",Thread.currentThread().getName(),++count));
obj.notifyAll();
obj.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} //ReentrantLock 重入锁
public void myPrint2(){
//一进入就加锁
lock.lock();
try{
while(count<limit){
System.out.println(String.format("线程[%s]打印数字:%d",Thread.currentThread().getName(),++count));
condition.signalAll();//唤醒被锁住的线程
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//最后释放锁
lock.unlock();
}
} public static void main(String[] args) throws InterruptedException {
OddEvenPrinter print = new OddEvenPrinter(10, 0);
System.err.println("-----------第一种方法 Object-----------");
Thread thread1 = new Thread(print::myPrint1, "thread-A");
Thread thread2 = new Thread(print::myPrint1, "thread-B");
thread1.start();
thread2.start();
Thread.sleep(1000); System.err.println("-----------第二种方法 lock-----------");
Thread thread3 = new Thread(print::myPrint2, "thread-C");
Thread thread4 = new Thread(print::myPrint2, "thread-D");
thread3.start();
thread4.start();
Thread.sleep(1000);
} }

多线程通信的两种方式? (可重入锁ReentrantLock和Object)的更多相关文章

  1. “全栈2019”Java多线程第二十九章:可重入锁与不可重入锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  3. 轻松学习java可重入锁(ReentrantLock)的实现原理

    转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...

  4. 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  5. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  6. 17_重入锁ReentrantLock

    [概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...

  7. Java 显示锁 之 重入锁 ReentrantLock(七)

    ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...

  8. java 可重入锁ReentrantLock的介绍

    一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...

  9. 不同VLAN之间相互通信的两种方式

    (单臂路由.三层交换) 试验环境:东郊二楼第三机房 试验设备:Catalyst 2950-24(SW3) Cisco 2611(R2) Catalyst 3750 SERIES (带两个SD接口,S8 ...

随机推荐

  1. Linux系统学习 十九、VSFTP服务—虚拟用户访问—为每个虚拟用户建立自己的配置文件,单独定义权限

    为每个虚拟用户建立自己的配置文件,单独定义权限 可以给每个虚拟用户单独建立目录,并建立自己的配置文件.这样方便单独配置权限,并可以单独指定上传目录 1.修改配置文件 vi /etc/vsftpd/vs ...

  2. Java的反射机理

    Java反射是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的全部属性和方法等,调用方法以及访问属性,而且不需要提前在编译期知道运行的对象是什么 ...

  3. 冒泡排序(C语言)

    # include<stdio.h> int main(void) { int arr[10]={5,4,7,9,2,3,1,6,10,8}; //定义一个位排序的数组 int i; // ...

  4. tf.InteractiveSession()与tf.Session()的区别

    Tensorflow依赖于一个高效的C++后端来进行计算.与后端的这个连接叫做session.一般而言,使用TensorFlow程序的流程是先创建一个图,然后在session中启动它. 这里,我们使用 ...

  5. linux的装配与虚拟机的快照

    一.科普 1969年,“c语言之父”,“b语言之父”,ken Thompson,开发了一个叫unics系统,是unix系统的雏形,只不过此时的UNICS是用汇编语言写的.移植到其它计算机上需要改很多源 ...

  6. 微信小程序的坑(持续更新中)

    参与微信小程序开发有一段时间了,先后完成信息查询类和交易类的两个不同性质的小程序产品的开发:期间遇到各种各样的小程序开发的坑,有的是小程序基础功能不断改进完善而需要业务持续的适配,有的是小程序使用上的 ...

  7. js中触发表单提交

    html代码: <form action="/test/action" method="get" id="myForm"> &l ...

  8. PHP捕获异常register_shutdown_function和error_get_last的使用

    register_shutdown_function 注册一个会在php中止时执行的函数,注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用. error_get_last ...

  9. JeeSite | 保存信息修改记录封装

    前面写过两篇关于“保存信息修改记录”的内容,分别如下: JeeSite | 保存信息修改记录 JeeSite | 保存信息修改记录续 回顾         第一篇文章通过类字段的比较返回一个有字段值不 ...

  10. 巧妙利用label标签实现input file上传文件自定义样式

    提到上传文件,一般会想到用input file属性来实现,简单便捷,一行代码即可    但input file原生提供的默认样式大多情况下都不符合需求,且在不同浏览器上呈现的样式也不尽相同   我们往 ...