1.从乐观锁和悲观锁开始说起
2.synchronized的8锁案例
2.1 第一种情况:两个线程锁的是同一个实例对象
2.2 第二种情况:第一个线程的逻辑中添加sleep睡眠
2.3 第三种情况:第二个线程执行的是无锁方法
2.4 第四种情况:两个线程锁的是两个不同的实例对象
2.5 第五种情况:两个线程锁的是同一个类对象
2.7 第七种情况:一个线程锁实例对象,一个线程锁类对象
3.字节码角度分析synchronized
3.1 synchronized同步代码块
3.2 synchronized同步实例方法
3.3 synchronized同步静态方法
3.4 synchronized锁的是什么?
5.1 可重入锁之隐式锁synchronized
5.2 可重入锁之显式锁Lock
悲观锁的实现方式:① synchronized关键字;
② Lock接口的实现类都是悲观锁。
适合写操作多的场景,先加锁可以保证写操作时数据正确。显示的锁定之后再操作同步资源。
- public synchronized void method() {
- //加锁之后的业务逻辑
- }
-
- Lock lock = new ReentrantLock();
-
- public void method2() {
- lock.lock();
- try {
- //加锁之后的业务逻辑
- } finally {
- lock.unlock();
- }
- }
乐观锁的实现方式:① 版本号机制Version。 (只要有人提交了就会修改版本号,可以解决ABA问题)
ABA问题:再CAS中想读取一个值A,想把值 A变为C,不能保证读取时的A就是赋值时的A,中间可能有个线程将A变为B再变为A。
解决方法:Juc包提供了一个AtomicStampedReference,原子更新带有版本号的引用类型,通过控制版本值的变化来解决ABA问题。
② 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
适合读操作多的场景,不加锁的性能特点能够使其操作的性能大幅提升。
- AtomicInteger atomicInteger = new AtomicInteger(1);
- atomicInteger.incrementAndGet();
首先,我们可以看一下阿里巴巴Java开发手册中,关于锁的强制性要求。

这里我能使用 Lambda 表达式的原因是,Phone类中的这两个实例方法是无参、无返回值的,和Runnable中的run方法一致,所以直接方法引用是OK的。
两个线程锁的都是我 new 的同一个对象 phone,所以当第一个线程去发邮件的时候就拿到了 phone 对象这把锁,此时第二个线程就拿不到了,只能等待第一个线程执行完释放锁,它才可以去发短信。
- package com.juc.lock;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- *
- */
- class Phone {
- public void sendEmail() {
- synchronized (this) {
- System.out.println("-----发送邮件");
- }
- }
-
- public void sendSMS() {
- synchronized (this) {
- System.out.println("-----发送短信");
- }
- }
- }
-
- public class Lock8 {
-
- public static void main(String[] args) {
- Phone phone = new Phone();
-
- new Thread(phone::sendEmail, "a").start();
-
- try {
- TimeUnit.MILLISECONDS.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(phone::sendSMS, "b").start();
- }
- }

和第一种情况不同的是:当第一个线程拿到 phone 对象锁之后,在发邮件的过程中,sleep睡眠了2秒。但是执行结果和第一种情况是一样的。
原因就是 sleep 方法并不会释放锁,只是让线程暂定一段时间,一段时间过后线程照常执行(不要interrupt打断。。。)。
某一个时刻内,只能有唯一的一个线程去访问这些针对于实例对象的synchronized方法, 锁的是当前对象this ,被锁定后,其它的线程都不能 进入到当前对象的其他synchronized方法。
- package com.juc.lock;
-
- import java.util.concurrent.TimeUnit;
-
- /**
- *
- */
- class Phone {
- public void sendEmail() {
- synchronized (this) {
- try {
- TimeUnit.SECONDS.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("-----发送邮件");
- }
- }
-
- public void sendSMS() {
- synchronized (this) {
- System.out.println("-----发送短信");
- }
- }
- }
-
- public class Lock8 {
-
- public static void main(String[] args) {
- Phone phone = new Phone();
-
- new Thread(phone::sendEmail, "a").start();
-
- try {
- TimeUnit.MILLISECONDS.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- new Thread(phone::sendSMS, "b").start();
- }
- }