2025-06-30 20:30:21Java锁到底是个什么东西
一、java锁存在的必要性
要认识java锁,就必须对2个前置概念有一个深刻的理解:多线程和共享资源。
对于程序来说,数据就是资源。
在单个线程操作数据时,或快或慢不存在什么问题,一个人你爱干什么干什么。
多个线程操作各自操作不同的数据,各干各的,也不存在什么问题。
多个线程对共享数据进行读取操作,我就四处看看,什么也不动,也不存在什么问题。
但如果多个线程对共享数据进行写操作,问题就来了。
经典库存问题:
mysql 记录剩余:1,redis 缓存记录剩余:1。
小明上网下单,后台程序检查 redis 记录存货剩 1 台,数据库执行 -1,但小明网太卡了,数据库刚执行完 -1,redis 没来得及更新成0,小红的华为5G直接下单,redis 剩1台,数据库-1,redis -1,下单成功一气呵成。结果就是2个人买了同一台手机。
这种业务场景可以说比比皆是,所以要解决这种数据同步问题就要有对应的办法,所以发明了java锁这个工具来保证数据的一致性,举个例子:
在一个不分男女的公共厕所中上一把锁,有人进去,把门锁住,上完出来,把锁打开,以此类推。
二、2个重要的java锁
synchronized关键字
synchronized关键字是java开发人员最常用的给共享资源上锁的方式,也基本可以满足一般的进程同步要求,使用 synchronized 无需手动执行加锁和释放锁的操作,只需在需要同步的代码块、普通方法、静态方法上加入该关键字即可,JVM 层面会帮我们自动的进行加锁和释放锁的操作。
修饰普通方法
/**
* synchronized 修饰普通方法
*/
public synchronized void method() {
// ....
}
当 synchronized 修饰普通方法时,被修饰的方法被称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。
修饰静态方法
/**
* synchronized 修饰静态方法
*/
public static synchronized void staticMethod() {
// .......
}
当 synchronized 修饰静态方法时,其作用范围是整个程序,这个锁对于所有调用这个锁的对象都是互斥的。
修饰普通方法 VS 修饰静态方法
创建一个类,其中有synchronized修饰的普通方法和synchronized修饰的静态方法。
public class SynchronizedUsage {
/**
* synchronized 修饰普通方法
*/
public synchronized void method() {
System.out.println("普通方法执行时间:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* synchronized 修饰静态方法
*/
public static synchronized void staticMethod() {
System.out.println("静态方法执行时间:" + LocalDateTime.now());
try {
// 休眠 3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试
public class Test01 {
/**
* 创建线程池同时执行任务
*/
static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
// 执行两次静态方法
threa