synchronized
Synchronized原理是什么?
1. 简介
在多线程编程中,为了确保共享资源的安全性,我们需要使用同步机制。其中,synchronized
关键字是Java语言提供的一种同步机制,用于保护临界区,防止多个线程同时访问共享资源而出现数据不一致或异常的情况。
本文将介绍synchronized
关键字的工作原理,并通过示例代码演示其在多线程环境下的使用。
2. synchronized
关键字的使用
在Java中,我们可以使用synchronized
关键字来修饰方法或代码块,以实现同步。其使用形式如下:
- 同步方法:使用
synchronized
修饰方法,示例代码如下:
public synchronized void synchronizedMethod() {
// 临界区代码
// ...
}
- 同步代码块:使用
synchronized
关键字修饰代码块,指定需要同步的对象,示例代码如下:
public void synchronizedBlock() {
// 非临界区代码
// ...
synchronized (object) {
// 临界区代码
// ...
}
// 非临界区代码
// ...
}
在上述示例中,synchronizedMethod()
方法和synchronizedBlock()
方法中的临界区代码将会被同一时刻只允许一个线程执行,其他线程需要等待。
3. synchronized
原理
synchronized
关键字的原理基于Java对象头和内置锁(Intrinsic Lock)。
3.1 Java对象头
在Java虚拟机中,每个对象都有一个对象头,用于存储对象自身的运行时数据,包括对象的哈希码、锁状态标志等信息。对于被synchronized
关键字修饰的方法或代码块,虚拟机会使用对象头中的一部分数据来实现锁的获取与释放。
3.2 内置锁
每个Java对象都可以关联一个内置锁,也称为监视器锁(Monitor Lock)。内置锁是对对象进行加锁和解锁的机制,可以确保在同一时间只有一个线程可以获取到该对象的锁。被加锁的对象只能被加锁线程访问,其他线程必须等待锁的释放。
当一个线程尝试获取一个对象的锁时,如果该锁被其他线程占用,则该线程会被阻塞,直到锁被释放。
3.3 synchronized
实现原理
synchronized
关键字的实现原理可以分为三种状态:无锁状态、偏向锁状态和重量级锁状态。
无锁状态:如果一个对象没有被任何线程锁定,那么它的对象头中的锁标志位为“无锁状态”。此时,可以通过CAS(Compare and Swap)操作直接获取锁,无需额外的同步操作。
偏向锁状态:第一个获取锁的线程会把对象头中的锁标志位设置为“偏向锁状态”。在偏向锁状态下,如果有其他线程尝试获取锁,会通过CAS操作判断是否可以直接获取锁。如果可以,则进行偏向锁的撤销,否则转为重量级锁状态。
重量级锁状态:如果对象头中的锁标志位为“重量级锁状态”,说明锁已被其他线程占用。此时,获取锁的线程会进入阻塞状态,直到持有锁的线程释放锁。
synchronized
关键字的原理是通过对象头中的锁标志位和内置锁来实现线程同步。
4. synchronized
的示例代码演示
为了更好地理解synchronized
的原理,我们将通过一个示例代码演示其在多线程环境中的使用。
public class SynchronizedDemo {
private int count = 0;
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
demo.start();
}
public void start() {
// 创建两个线程并启动
Thread thread1 = new Thread(new MyRunnable());
Thread thread2 = new Thread(new MyRunnable());
thread1.start();
thread2.start();
// 主线程等待两个线程执行完毕
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终的计数结果
System.out.println("Count: " + count);
}
// 自定义的Runnable实现类
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
// 使用synchronized修饰临界区
synchronized (this) {
count++;
}
}
}
}
}
在上述示例代码中,我们创建了一个SynchronizedDemo
类,其中包含一个count
变量。我们创建了两个线程,并分别启动这两个线程。每个线程都会对count
变量进行10000次递增操作。由于count
是一个共享资源,为了确保多个线程安全地访问它,我们使用了synchronized
关键字将临界区代码块进行同步。
运行以上代码,输出的结果应为20000。
5. 总结
本文介绍了synchronized
关键字的原理,它是Java多线程编程中常用的同步机制。通过synchronized
关键字的修饰,我们可以保护临界区,防止多个线程同时访问共享资源,从而确保线程安全。
synchronized
的原理基于Java对象头和内置锁。对象头存储了锁的状态信息,内置锁用于保护对象的临界区。不同状态的锁(无锁状态、偏向锁状态、重量级锁状态)决定了线程获取锁的方式和行为。
虽然synchronized
是Java多线程编程中最基本和常用的同步机制,但在高并发场景下,它可能会导致性能下降。因此,在实际开发中,需要根据具体情况选择更适合的同步方式,如使用Lock
接口提供的互斥锁。
- 点赞
- 收藏
- 关注作者
评论(0)