Java 使用 synchronized 实现内置锁

多个进程或线程同时(或者说在同一段时间内)访问同一资源会产生并发(线程安全)问题。解决并发问题可以用锁。单机情况下可以直接使用synchronized关键字实现的内置锁。

每个Java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁保护的同步代码块或方法。

Java内置锁是一个互斥锁,这就意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果线程B不释放这个锁,那么线程A将永远等待下去。

Java的内置锁基本上可以分为对象锁 和类锁 ,对象锁是用于对象实例方法或者对象实例上的,类锁是用于类的静态方法或者类的class对象上的。我们知道,一个类的实例对象可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。

synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。

1. 对象锁

1.1. 同步方法

同步方法,是对该对象加锁,其他线程将无法访问需要获取该对象锁的方法和代码块。

java 复制代码
public class Test{
    
    public synchronized void print(){    
        System.out.println("hello world!");
    }
    
}

上面代码表示访问print()方法需要获取Test实例对象的锁。

1.2. 同步代码块(当前类对象锁)

同步代码块,这种写法也是锁住该类的实例对象,和1.1.效果一样,其他线程将无法访问需要获取该对象锁的方法和代码块。

java 复制代码
public class Test{
    
    public void print(){    
        synchronized(this){
            System.out.println("hello world!");
        }
    }
    
}

上面代码表示访问print()方法中的代码块需要获取Test实例对象的锁。

1.3. 同步代码块(传入类对象锁)

同步代码块,这种写法锁住传入的对象,不影响需要获取当前类对象锁的方法和代码块的访问。

java 复制代码
public class Test{
    
    private byte[] lock = new byte[0];
    
    public void print(){    
        synchronized(lock){
            System.out.println("hello world!");
        }
    }
    
    public synchronized void print1(){
        System.out.println("123456");
    }
    
}

执行print()方法里面的同步代码块,会给byte[]对象lock加锁,注意不是给Test的对象加锁,也就是说Test对象的其它synchronized方法和代码块不会因为print()而被锁。print1()方法可以正常访问。

2. 类锁

类锁修饰方法和代码块的效果和对象锁是一样的,因为类锁只是一个抽象出来的概念,类锁和对象锁是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

2.1. 同步静态方法

因为静态方法是所有对象实例共用的,对应着synchronized修饰的静态方法的锁也是唯一的,所以抽象出来个类锁。其他线程将无法访问需要获取该类锁的方法和代码块。

java 复制代码
public class Test{
    
    public synchronized static void print(){    
        System.out.println("hello world!");
    }
    
}

2.2. 同步代码块(当前类class锁)

这种写法,直接锁定当前类的class对象,因为每个类只有一个class对象,和2.1.效果一样。其他线程将无法访问需要获取该类锁的方法和代码块。

java 复制代码
public class Test{
    
    public void print(){    
        synchronized(Test.class){
            System.out.println("hello world!");
        }
    }
    
}
相关推荐
qq_3646821520 分钟前
录音APP怎么切换语言?多语言版本如何使用
java·服务器·前端
Wang's Blog24 分钟前
Java 项目实战: 交友项目 - 用户体系集成:根据环信id查询用户详情
java·lua·交友
摇滚侠29 分钟前
《On Java 中文版 基础卷》阅读笔记 复用 13
java·笔记·python
2601_9689007731 分钟前
玻璃深加工的工艺窗口:4 道工序的温度与时间参数边界
android·java·javascript
薛之谦_37 分钟前
用 Java 实现阿里云式 IAM 子用户管理:架构设计与表结构详解
java·python·阿里云
甜到心里的蛋糕1 小时前
抖音评论自动回复机器人部署实践:无头 Chrome + DeepSeek + NapCat(2C2G 云主机)
java·jvm·chrome·python·抖音·deepseek
Java爱好狂.1 小时前
阿里1658页Java面试题总结(含答案)
java·后端·高并发·架构师·java面试·java面试题·java八股文
Thanks_ks1 小时前
从 0.1+0.2 不等于 0.3 说起:聊聊浮点数精度问题
java·python·数值计算·精度丢失·bigdecimal·浮点数·ieee754
海鸥-w1 小时前
区域名称同步存储实现笔记
java·笔记
Rain的Java大神之路2 小时前
LeetCode 21 合并两个有序链表:一道 Easy 题,凭什么刷掉一半候选人?
java·后端·算法