什么是同步锁?

2024.10.04 · 4 minute read

同步锁和线程

Synchronization Lock,同步锁,或称监视锁,互斥锁。

它确保不同线程,安全访问共享资源。

一个线程,想执行以下代码时,需获取该对象的锁。该获取过程,由 JVM 自动控制:

public synchronized void method() {
// 方法体
}
// OR
synchronized(object) {
 // do something
}

多个进程,想同时执行下面的代码,即尝试获取同一个对象的锁,叫线程竞争。

竞争会导致 Blocked 阻塞状态([[Java多线程的故事#线程的状态]])。线程 B 已经持有了锁,线程 A 想尝试获得,但失败。JVM 会将它置于锁池 Lock Pool 中,并改变 A 状态为阻塞。

Lock Pool 中存放着,所有试图获取锁,但没有如愿的线程。

线程 A 在池中,会等待锁的释放,一旦释放,便恢复运行。该过程称为等待机制。

与之相对,持有锁的线程,执行完代码以后会释放锁,JVM 会唤醒等待的线程。称之为唤醒机制。

例子

class Scratch {

    public static void main(String[] args) {
        SharedResource reource = new SharedResource();
        Thread threadA = new Thread(() -> {
            reource.doSomething("Thread A");
        });
        Thread threadB = new Thread(() -> {
            reource.doSomething("Thread B");
        });
        threadA.start();
        threadB.start();
    }
}

class SharedResource {
    public synchronized void doSomething(String threadName) {
        System.out.println(threadName + " has entered the synchronized method");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(threadName + " has exited the synchronized method");
    }
}

输出:

Thread A has entered the synchronized method
Thread A has exited the synchronized method
Thread B has entered the synchronized method
Thread B has exited the synchronized method

关键字 volatile

synchronized 锁的是 object,定义是 is created using a class is said to be an instance of that class。

volatile 锁的是 Variables,更轻量。开销小,性能好。

直接的例子:

public class SynchronizedExample {
    private boolean flag = false;

    public synchronized void setFlag() {
        flag = true;
    }

    public synchronized boolean isFlag() {
        return flag;
    }
}
public class VolatileExample {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public boolean isFlag() {
        return flag;
    }
}

特性对比:

属性volatilesynchronized
可见性 Visibilityyesyes
有序性 Orderingyesyes
原子性 Atomicitynoyes

可见性 Visibility

  • 定义:指当一个线程修改了共享变量的值,其他线程能立即看到。
  • 原因:多核 CPU 和各自缓存之间出现的一致性问题。
  • 例子:线程 A 修改了变量 x 的值,但线程 B 可能仍然看到的是 x 的旧值。

有序性 Ordering

  • 定义:程序执行的顺序按照代码的先后顺序执行。
  • 原因:为提高性能,编译器和 CPU 会对指令进行重排,导致执行与书写顺序不同。
  • 例子:代码中,语句 1 在语句 2 之前,但实际执行时,颠倒顺序。

原子性 Atomicity

  • 定义:指一个操作是不可中断。即使在多线程中,操作一旦开始,会让整个操作完成,而不被其他线程干扰。
  • 问题:在多线程环境下,非原子操作可能会导致数据不一致。
  • 例子:i++ 看似是一个操作,实际上包含了「读取 i」、「增加 1」、「写回内存」三个步骤,因此不是原子操作。

什么时候用 volatile?

适合用在一个线程写,多个线程读的场景,并且写入是简单赋值,不依赖于变量当前值。

适合的例子:

public class FlagHolder {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;  // 简单赋值,不依赖当前值
    }

    public boolean isFlag() {
        return flag;
    }
}

不适合的例子:

public class Counter {
    private volatile int count = 0;

    public void increment() {
        count++;  // 不适合,因为这是复合操作,依赖当前值
    }
}

感谢您的阅读!您的支持是我的动力。

如果您喜欢这篇文章,不妨请我喝杯咖啡。 ☕️