Java并发编程:锁的释放

*/-->

code {color: #FF0000}
pre.src {background-color: #002b36; color: #839496;}

Java并发编程:锁的释放

上一篇线程的同步,我们讲了锁的获得方式。接下来,我们讲讲锁的释放。首先,锁定的方法或者代码块运行完毕,肯定会释放锁。
那么,主动释放锁,是通过什么方法来达到的呢?
如果,我们看过Object类的方法,我们会注意到它有三个奇怪的方法:wait(),notify()和notifyAll()。 它们就是用来释放锁的。

1 线程的状态

在讲锁的释放前,我们先讲一下线程的三种主要状态:运行、就绪(可运行)、阻塞。当然,除了这个之外,肯定还有初始状态和结束状态,那个就不讨论了。

当我们创建线程之后,还只是进入初始状态,如果我们调用run()方法运行,根本就不会启动新的线程。当调用start()后,可以将线程状态改为可运行状态,然后,由操作系统来决定调用哪个线程。当幸运的被操作系统选中之后,就可以进入真正的运行状态了。当运行当时间片用完,或者调用yield()礼让方法,就会把当前当执行权交出来,进入可运行就绪状态。如果在运行的过程中,有系统IO,如等待输入,弹出确认对话框等,则会使当前线程进入阻塞状态,动弹不得。只有等待用户操作之后,才能往下进行。sleep()方法和join()方法可以可以达到类似的阻塞效果。


然后,我们把对象锁部分的状态也加进来。当我们使用synchronized修饰方法或者代码块时,会获取对象的锁,并进入获取该对象锁的等待池,由操作系统来决定调用哪个线程(非公平锁)。当获取到该对象锁之后,就可以进入可运行状态了。
另外,还有一个对象的wait()方法,可以使线程放弃持有的该对象锁,并进入通知等待状态。当其他线程调用等待线程需要的对象的notify()或者notifyAll()方法时,该线程重新进入获取对象锁的队列中参与锁的获取。

2 wait() notify() 和 notifyAll()

synchronized获取锁的方法我们已经详细的讲解过了,我们接下来来看一下如何主动释放锁。假设,我们想创建两个线程,让这两个线程,依次做一件事情,而不是同时启动之后,就由操作系统来决定它们的执行顺序,那么,我们该怎么做呢?
那就是首先请求前一个线程的锁,然后,获取自己线程的锁,再释放自己的锁,并通知这个锁对象的等待队列(下一个线程!哎哎,醒醒别睡啦,起来干活啦!),释放前一个线程的锁,并进入等待前一个锁对象的通知队列。

用代码展示为:

public class SyncDemo implements Runnable {
private String name;
private Object prev;
private Object cur; public SyncDemo(String name, Object prev, Object cur) {
this.name = name;
this.prev = prev;
this.cur = cur;
} @Override
public void run() {
int count = 10;
while (count > 0) {
synchronized(prev) {
synchronized(cur) {
System.out.println(name);
count--;
cur.notify();
}
try {
prev.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
SyncDemo r1 = new SyncDemo("A", b, a);
SyncDemo r2 = new SyncDemo("B", a, b); new Thread(r1).start();
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(r2).start();
}
}

写得更通用一点,支持更多的线程依次执行:

public static void main(String[] args) {
int num = 5; Object[] locks = new Object[num];
String[] names = {"A", "B", "C", "D", "E"};
SyncDemo[] runnable = new SyncDemo[num]; for (int i = 0; i < num; i++) {
locks[i] = new Object();
} for (int i = 0; i < num; i++) {
runnable[i] = new SyncDemo(names[i], locks[(i - 1 + num) % num] , locks[i]);
} for (int i = 0; i < num; i++) {
new Thread(runnable[i]).start();
try {
Thread.sleep(100);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Date: 2017-07-05 22:31

Author: WEN YANG

Created: 2017-07-07 Fri 21:02

Emacs 25.2.1 (Org mode 8.2.10)

Validate

Java并发编程:锁的释放的更多相关文章

  1. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  2. Java并发编程锁之独占公平锁与非公平锁比较

    Java并发编程锁之独占公平锁与非公平锁比较 公平锁和非公平锁理解: 在上一篇文章中,我们知道了非公平锁.其实Java中还存在着公平锁呢.公平二字怎么理解呢?和我们现实理解是一样的.大家去排队本着先来 ...

  3. Java并发编程锁系列之ReentrantLock对象总结

    Java并发编程锁系列之ReentrantLock对象总结 在Java并发编程中,根据不同维度来区分锁的话,锁可以分为十五种.ReentranckLock就是其中的多个分类. 本文主要内容:重入锁理解 ...

  4. Java 并发编程 | 线程池详解

    原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...

  5. 【Java并发编程实战】----- AQS(二):获取锁、释放锁

    上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放. AQS锁获取 AQS包含如下几个方法: acquire(int arg):以独占模式获取对象,忽略中断. acquireInte ...

  6. 转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

    简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...

  7. [转载] java并发编程:Lock(线程锁)

    作者:海子 原文链接: http://www.cnblogs.com/dolphin0520/p/3923167.html 出处:http://www.cnblogs.com/dolphin0520/ ...

  8. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  9. Java并发编程(十一)-- Java中的锁详解

    上一章我们已经简要的介绍了Java中的一些锁,本章我们就详细的来说说这些锁. synchronized锁 synchronized锁是什么? synchronized是Java的一个关键字,它能够将代 ...

随机推荐

  1. express中app和router的区别

      var app = express(); var router = express.Router(); 以上二者的区别是什么,什么时候用哪个最合适? 区别看下面的例子: app.js var ex ...

  2. 020:reverse函数补充

    补充reverse两点: 1.如若在反转url时,需要添加参数,那么可以传递 kwargs 参数到 reverse 函数中,实例代码如下: '}) ) 2.如若想添加查询字符串参数,则必须手动进行ur ...

  3. SQL基础-order by

    若sql语句中order by指定了多个字段,则怎么排序? 举个例子吧:order by id desc,time desc 先是按 id 降序排列  (优先)如果 id 字段 有些是一样的话   再 ...

  4. Prometheus 后续杂记

    在后续prometheus的使用中遇到的一些问题我会在此记录 搭建初期几个问题 rule.yml中对每条告警加上主机名? 要在告警通知中加上故障机器主机名不能从prometheus的采集监控项数据中的 ...

  5. 从Java中的length和length()开始

    1.在没有IDE自动补齐的情况下,怎样得到数组的长度?怎样得到字符串的长度? int[] arr = new int[3]; System.out.println(arr.length);//leng ...

  6. 图的深度优先搜索(DFS)和广度优先搜索(BFS)算法

    深度优先(DFS) 深度优先遍历,从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接 ...

  7. mysql中查看ef或efcore生成的sql语句

    http://www.solves.com.cn/it/sjk/MYSQL/2019-07-01/1336.html 涉及命令 1.开启general log模式 MySQL>set globa ...

  8. day68—angularJS学习笔记之-过滤器

    转行学开发,代码100天——2018-05-23 今天学习angularJS的过滤器的使用. angular中的常用过滤器用来修改数据格式,主要有以下几类: 1.大写,| uppercase 2.小写 ...

  9. Selenium学习之==>三种等待方式

    在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果你不做任何处理的话,代码会由于没有找到元素,而报错.这时我们就要用到wait(等待),而在Selenium中,我们可以用到一共三种等待, ...

  10. Python2.7安装&配置环境变量

    python安装版本为2.7 下载安装包,双击安装,一路按照提示进行. 安装完成后,配置环境变量. 我的电脑—属性--高级系统设置—高级—环境变量--Path--编辑(将安装路径粘贴进去),添加到安装 ...