目录
1 synchronized 的特性
1)互斥
2) 刷新内存(内存可见性)
3) 可重入
2 synchronized 使用示例
1) 直接修饰普通方法:
2) 修饰静态方法:
3) 修饰代码块:
.3 Java 标准库中的线程安全类
1 synchronized 的特性
1)互斥
synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到 同一个对象 synchronized 就会阻塞等待.
进入 synchronized 修饰的代码块, 相当于 加锁
退出 synchronized 修饰的代码块, 相当于 解锁
synchronized用的锁是存在Java对象头里的。
synchronized的底层是使用操作系统的mutex lock实现的.
理解 " 阻塞等待".
- 针对每一把锁, 操作系统内部都维护了一个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试进行加锁, 就加不上了, 就会阻塞等待, 一直等到之前的线程解锁之后, 由操作系统唤醒一个新的线程, 再来获取到这个锁.
注意 :
- 上一个线程解锁之后, 下一个线程并不是立即就能获取到锁. 而是要靠操作系统来 "唤醒". 这也就是操作系统线程调度的一部分工作.
- 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能 获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则.
2) 刷新内存(内存可见性)
synchronized 的工作过程:
- 获得互斥锁
- 从主内存拷贝变量的最新副本到工作的内存
- 执行代码
- 将更改后的共享变量的值刷新到主内存
- 释放互斥锁
所以 synchronized 也能保证内存可见性. 与 volatile相似。
3) 可重入
代码示例
- 在下面的代码中, increase 和 increase2 两个方法都加了 synchronized, 此处的 synchronized 都是针对 this 当前 对象加锁的.
- 在调用 increase2 的时候, 先加了一次锁, 执行到 increase 的时候, 又加了一次锁. (上个锁还没释放, 相当于连续加两次锁)
//一,针对this对象
static class Counter {public int count = 0;synchronized void increase() {count++;}synchronized void increase2() {increase();}
}
//二,针对lockerprivate static Object locker = new Object();Thread t1 = new Thread(() -> {synchronized (locker1) {// 此处的 sleep 很重要. 要确保 t1 和 t2 都分别拿到一把锁之后, 再进行后续动作.synchronized (locker1) {System.out.println("t1 加锁成功!");}}});
这个代码是完全没问题的. 因为 synchronized 是可重入锁.
在可重入锁的内部, 包含了 "线程持有者" 和 "计数器" 两个信息.
- 如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取 到锁, 并让计数器自增.
- 解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到)
2 synchronized 使用示例
synchronized 本质上要修改指定对象的 "对象头". 从使用角度来看, synchronized 也势必要搭配一个具体的对象来使用.
1) 直接修饰普通方法:
锁的 SynchronizedDemo 对象
public class SynchronizedDemo {public synchronized void methond() {}}
2) 修饰静态方法:
锁的 SynchronizedDemo 类的对象
public class SynchronizedDemo {public synchronized static void method() {}}
3) 修饰代码块:
//1. 锁当前this对象public class SynchronizedDemo {public void method() {synchronized (this) {}}}//2.锁类对象public class SynchronizedDemo {public void method() {synchronized (SynchronizedDemo.class) {}}}
两个线程竞争同一把锁, 才会产生阻塞等待.
.3 Java 标准库中的线程安全类
这些是线程安全的. 使用了一些锁机制来控制.
- Vector (不推荐使用)
- HashTable (不推荐使用)
- ConcurrentHashMap
- StringBuffer
核心方法都带有 synchronized .
结语:synchronized的相关分享到这里就结束了,希望对大家的学习会有帮助,如果大家有什么问题或者不同的见解,欢迎大家评论区的留言, 感谢支持 !!!