Java线程锁,synchronized、wait、notify详解
(原)
JAVA多线程这一块有点绕,特别是对于锁,对锁机制理解不清的话,程序出现了问题也很难找到原因,在此记录一下线程的执行以及各种锁。
1、JAVA中,每个对象有且只有一把锁(lock),也叫监视器(monitor)。
2、同步(synchronized),synchronized可以修饰的方法或方法中的对象。
3、如果有一个线程进入到了synchronized方法修饰的对象,那么它将会获得这个对象的唯一一把锁,在该线程没有交出这把锁的时候,其它线程是无法访问到该方法中的。该线程会在执行完synchronized方法块中的内容后交出对象锁。

4、关于wait,notify,属于Object类,并且无法被重写,(网上JDK的1.5和1.6有中文版的API,对于这一块的翻译基本都是机器翻译,很不准确。建议看原版的英文说明文档)。
wait:
4.1、wait方法The current thread must own this object's monitor,当前线程必需获得这个对象的锁。因为一个线程进入了synchronized的代码块表示这个线程拿到了对象锁,那么这个wait方法必需在synchronized代码块中。
4.2、这个方法让进入到此处的线程丢掉对象锁并且挂起等待(能执行到wait方法的线程一定是拿到了对象锁的线程,如果不理解,请看4.1)。
4.3、其它线程调用这个对象的notify或notifyAll方法时,系统会在当前挂起等待在wait方法处的多个线程中,随机找出一个唤醒,被唤醒的线程会等待直到它拿到了对象锁并继续执行。
4.4、一个线程可能在通知、打断或超时之前被唤醒,这就是所谓的超时欺骗唤醒。实际情况下会很少出现这种情况,应用程序必需防范着判断条件使该线程被唤醒,如果条件不满足需要该线程继续等待。换句话说,wait方法必需放在循环里面。像下面这样。
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
这句话即使看明白了,也有些难理解,画个图:
4.4.1、比如有线程1进到了synchronzied,拿到了对象锁,此时其它线程都无法进法该synchronized方法块中。

4.4.2、当线程1执行到wait方法时,会丢掉手上的锁,这时其它线程就能进来了,然后线程1会在此处挂起,不再执行,直到有其它线程调用了这个对象的notify或notifyAll方法。

4.4.3、此时线程2和线程3以同样的方法来到了wait方法处等待。

4.4.4、然后又来了一个线程4,进入了该对象的另一个方法,并且调用了该对象的notify方法。

4.4.5、对象notify被调用后,wait方法处等待的线程中有一个有机会被唤醒,得到锁并继续往下执行。其它线程依旧会在原处等待。

notify:
Wakes up a single thread that is waiting on this object's monitor 唤醒一个在wait方法处等待的线程。
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. 这个被唤醒的线程不会被执行,直到它得到了这个对象锁。
The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object 被唤醒的线程将会与其它线程去竞争对象锁。(表示被唤醒不代表着马上会执行,除非该线程拿到对象锁。)
Only one thread at a time can own an object's monitor.只有一个线程在这此时得到这个对象锁。 This method should only be called by a thread that is the owner of this object's monitor 这个方法需要被获得了对象锁的线程去调用。
获取对象锁有三种方法:
By executing a synchronized instance method of that object 执行了对象的同步方法。
By executing the body of a {@code synchronized} statement that synchronizes on the object 执行了这个对象同步代码块。
For objects of type {@code Class,} by executing a synchronized static method of that class.对于Class,执行了一个静态的synchronized方法(这表示锁住了class)。
最后解释一下关于下面这段代码,文档中为什么要我们用while,而不是if 或是不加判断之类的
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
在上面的例子中,如果这里的条件是
if(num == 1){
wait();
}
num++;
notify();
如果三个线程进到wait方法时num为0,
我需要num 为 1时,一直等待,直到它为0才能让num+1
线程2获得了锁,当它执行到notify方法时,num变成了1,此时如果线程1被唤醒得到对象锁后,应该是重新做判断num == 1,如果num为1,还是得继续等待挂起。
如果这里用的是if,那么它表示该线程1在进if 前是满足num ==1这个条件的,但是出if判断时却是不满足num ==1这个条件的。根据已知我需要num 为 1时,一直等待,所以这里的if判断是不对的。
如果这里换成while,那么当线程1被唤醒得到线程锁,它还是得重新做判断,如果num == 1那么这个线程不将继续等待。
Java线程锁,synchronized、wait、notify详解的更多相关文章
- java线程池的使用与详解
java线程池的使用与详解 [转载]本文转载自两篇博文: 1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html ...
- Java线程创建形式 Thread构造详解 多线程中篇(五)
Thread作为线程的抽象,Thread的实例用于描述线程,对线程的操纵,就是对Thread实例对象的管理与控制. 创建一个线程这个问题,也就转换为如何构造一个正确的Thread对象. 构造方法列表 ...
- Java线程池七个参数详解
Java多线程开发时,常常用到线程池技术,这篇文章是对创建java线程池时的七个参数的详细解释. 从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize.maximumPoolS ...
- Java线程池(ThreadPool)详解
线程五个状态(生命周期): 线程运行时间 假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间. 如果:T1 + T3 远大于 T2,则可以 ...
- 关于java线程锁synchronized修饰普通方法与静态方法的区别
最近研究线程方面问题,关于这个synchronized锁修饰的问题,先是修饰普通方法,然后通过两个线程,各自执行自己对象的锁,发现方法执行互不影响,代码如下: private static int n ...
- Java精通并发-synchronized关键字原理详解
关于synchronized关键字原理其实在当时JVM的学习[https://www.cnblogs.com/webor2006/p/9595300.html]中已经剖析过了,这里从研究并发专题的角度 ...
- java线程池ThreadPoolExecutor类使用详解
在<阿里巴巴java开发手册>中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量:另一方面线程的细节管理交给线 ...
- Java线程的五种状态详解
状态转换图 1.new状态:通过new关键字创建了Thread或其子类的对象 2.Runnable状态:即就绪状态.可从三种状态到达,new状态的Thread对象调用start()方法,Running ...
- java线程基础知识----SecurityManager类详解
在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...
- 【多线程】Java线程池七个参数详解
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param coreP ...
随机推荐
- Android:剖析源码,随心所欲控制Toast显示
前言 Toast相信大家都不会陌生吧,如果对于Toast不甚了解,可以参考我的上一篇博客<Android:谈一谈安卓应用中的Toast情节>,里面有关于Toast基础比较详细的介绍.但是如 ...
- Java基础1:深入理解Java面向对象三大特性
更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...
- 产品研发团队如何融合OKR与Scrum敏捷开发?
「 OKR 」现在非常的火爆,很多公司都在使用,不仅国外的 Google.英特尔等大公司在用,国内的一线知名互联网企业今日头条和一些创业团队也都在使用. 那为什么「 OKR 」这么受欢迎呢,因为把它可 ...
- 版本管理工具Git(二)GitLab部署和配置
安装 # 安装依赖包 sudo yum install -y curl policycoreutils-python openssh-server # 启用并启动SSHD sudo systemctl ...
- 行为驱动:Cucumber + Selenium + Java(二) - 第一个测试
在上一篇中,我们搭建好了Selenium + Cucumber + Java的自动化测试环境,这一篇我们就赶紧开始编写我们的第一个BDD测试用例. 2.1 创建features 我们在新建的java项 ...
- ThreadLocal使用和原理简析
1. 解决共享资源冲突 对于并发工作,需要某种方式来防止两个任务同时访问相同的资源,至少在关键阶段不能出现这种冲突情况. 方法之一就是当资源被一个任务使用时,在其上加锁.第一个访问某项资源的任务必须锁 ...
- mongodb副本集高可用架构
一.简介 Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点. Mongodb Driver(客户端)的所有数据都写入Primary,Sec ...
- PE知识复习之PE新增节
PE知识复习之PE新增节 一丶为什么新增节.以及新增节的步骤 例如前几讲.我们的PE文件在空白区可以添加代码.但是这样是由一个弊端的.因为你的空白区节属性可能是只读的不能执行.如果你修改了属性.那么程 ...
- BEAMER中 Package Listings Warning: Text dropped after begin of listing on input line xx
Latex Beamer 模板 frame 中使用 listings package时总出现标题所示错误,无法前进.解决方法是在\begin{frame}后加上一个选项,即:\begin{frame} ...
- 【.NET Core项目实战-统一认证平台】第十四章 授权篇-自定义授权方式
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何强制令牌过期的实现,相信大家对IdentityServer4的验证流程有了更深的了解,本篇我将介绍如何使用自定义的授权方 ...