-
官方解释:同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见, 则对该对象变量的所有读取或写入都是通过同步方法完成的。
-
通俗解释:能够保证在
同一时刻
最多只有一个
线程执行该段代码,以达到保证并发安全的效果。
-
Synchronized是
Java
关键字,被Java语言原生支持 -
是
最基本
的互斥同步手段 -
是并发变成中的
元老级
角色,是并发编程的必学
内容
-
代码实战: 两个线程同时a++,最后结果会比预计的少
-
原因: a++,它看上去只是一个操作,实际上包含了三个动作:
- 读取a
- 将a+1
- 将a的值写到内存中
-
对象锁: 包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
`SynchronizedObjectCodeBlock2` `SynchronizedObjectMethod3`
-
类锁: 指synchronized修饰静态的方法或指定锁为Class对象
- 概念:Java类可能又很多对象,但只有一个Class对象
- 本质:所以所谓的类锁,不过是Class对象的锁而已。
- 用法和效果:类锁只能在同一时刻被一个对象拥有。
- 形式1:synchronized加在static方法上
- 形式2:synchronized(*.class)代码块
- 概念:Java类可能又很多对象,但只有一个Class对象
-
1.两个线程同时访问一个对象的同步方法
-
2.两个线程访问的是两个对象的同步方法
-
3.两个线程访问的是synchronized的静态方法
-
4.同时访问同步方法和非同步方法
SynchronizedYesAndNo6
-
5.访问同一对象的不同的普通同步方法
SynchronizedYesAndNo7
-
6.同时访问静态synchronized和非静态
-
7.方法抛异常后,会释放锁。
-
1.一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应第1 5 情况)
-
2.每个示例都对应有自己的一把锁,不同示例之间互不影响;例外: 锁对象是*.class以及synchronized修饰的是static方法的时候,所有的对象公用同一把类锁(对应第2 3 4 6种情况)
-
3.无论是方法正常秩序完毕或者方法抛出异常,都会释放锁(对应第7种情况)
-
可重入
-
什么是可重入: 指的是统一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁
-
好处:避免死锁 提升封装性
-
粒度:线程而非调用(用3种情况来说明和pthread的区别)
- 情况1:证明同一个方法是可重入的
- 情况2:证明可重入不要求是同一个方法
- 情况3:证明可重入不要求是同一个类中的
-
-
不可中断
- 什么事不可中断:一旦这个锁被别人获得了,如果我还想获得,我只能选择等待或阻塞,直到别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远的等下去。 相比之下,未来会介绍Lock类,可以拥有中断的能力,第一点,如果我觉得我等的时间太长了,有权中断现在已经获取到锁的线程的执行 第二点,如果我觉我等待的时间太长了不想再登了,也可以退出。
-
加锁和释放锁的原理
- 现象:
- 获取和释放锁的时机:内置锁
- 等价代码
- 深入JVM看字节码:反编译 monitor的指令
- 概况
- 反编译
javac Decompliation14.java
javap -verbose Decompliation14.class
- Monditorenter和Monditorexit指令
-
可重入原理:加锁次数计数器
- JVM负责跟踪对象被加锁的次数
- 线程第一次给对象加锁的时候,计数变为1.每当这个相同的线程在此对象上再次获得锁时,计数会增加
- 每当任务离开时,计数递减,当计数为0的时候,锁被完全释放
-
可见性原理:Java内存模型
- 效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程
- 不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象) 可能是不够的
- 无法知道是否成功获取到锁
-
1.使用注意点:锁对象不能为空,作用于不宜过大,避免死锁。
-
2.如何选择Lock和Synchronized关键字?
有现成的工具类,优先使用工具类,没有则选择Synchronized,避免少写代码,如果需求要定义自定义的加解锁规则,则选择Lock
-
3.多线程访问同步方法的各种具体情况