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 ...
随机推荐
- springboot情操陶冶-jmx解析
承接前文springboot情操陶冶-@Configuration注解解析,近期笔者接触的项目中有使用到了jmx的协议框架,遂在前文的基础上讲解下springboot中是如何整合jmx的 知识储备 J ...
- Docker容器监控(十)--技术流ken
docker自带的监控命令 docker自带了三个监控命令即ps, top, stats ps docker ps 可以帮助我们很快的了解当前正在运行的容器 -a:会显示已经停掉的容器 [root@h ...
- List<T>常用操作函数
1.Add():添加单个元素2.AddRange():添加一个集合4.Insert():插入一个元素5.InsertRange():插入一个集合6.Remove():移除指定的元素7.RemoveAt ...
- CentOS 7上安装Docker 1.8
Docker支持运行在以下CentOS版本: CentOS 7.X 安装在二进制兼容的EL7版本如 Scientific Linux也是可能成功的,但是Docker没有测试过并且不官方支持. 此文带你 ...
- Go中变量作用域的小坑
直接上一实例: package main import "fmt" func main(){ fmt.Println("for start") ; a < ...
- Ubuntu 18.04 安装java8
step1: 添加ppa sudo add-apt-repository ppa:webupd8team/java sudo apt-get update step2: 安装oracle-java-i ...
- GNOME图形界面的基本操作
成功登录进入CentOS系统之后,我们首先看到的桌面就是GNOME图形界面,下面来看一下相关的基本操作. 个性化设置 1,设置屏幕分辨率 进入菜单 2,更换桌面背景 进入下面菜单. 选择一张背景图片, ...
- oracle nvl,nvl2,coalesce几个函数的区别
1.nvl(exp1,exp2)该函数是处理表达式中的空值: 假设表达式exp1是空值,则该函数返回表达式exp2的值, 假设表达式exp1没有是空值,则该函数返回表达式exp1的值. 2.nvl2( ...
- jsp基础语言-jsp代码段
jsp代码段:是放在<% %>标记之间符合java语言规范的代码片段 格式:<% 代码段 %> 代码段中可以包含用于jsp变量和方法的声明.显示表达式.HTML以及调用Jav ...
- Python全栈之路(目录) - 含资料(持续更新)
一. Python全栈之路 - 目录 Python基础 Python进阶 网络编程 并发编程 前端 数据库 Python Web框架之Django 前端框架之Vue Linux Flask+智能玩具 ...