Java中synchronized的作用及用法

发布时间:2021-06-18 16:04:38 作者:chen
来源:亿速云 阅读:283
# Java中synchronized的作用及用法

## 一、引言

在多线程编程中,线程安全是一个核心问题。当多个线程同时访问共享资源时,如果没有适当的同步机制,就可能出现数据不一致、脏读等问题。Java提供了`synchronized`关键字来解决这类线程安全问题。本文将深入探讨`synchronized`的作用、用法及其实现原理。

---

## 二、synchronized的作用

### 1. 保证原子性
`synchronized`能够确保同一时刻只有一个线程执行被保护的代码块或方法,从而避免多线程环境下的竞态条件(Race Condition)。

```java
public class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++; // 原子操作
    }
}

2. 保证可见性

通过synchronized修饰的代码块或方法,在释放锁时会将工作内存中的变量刷新到主内存,获取锁时会从主内存读取最新值。

3. 保证有序性

synchronized通过互斥锁禁止指令重排序,确保代码执行顺序符合预期。


三、synchronized的用法

1. 同步实例方法

作用于当前实例对象,锁是当前实例(this)。

public synchronized void method() {
    // 线程安全代码
}

2. 同步静态方法

作用于类的Class对象,锁是当前类的Class对象(ClassName.class)。

public static synchronized void staticMethod() {
    // 线程安全代码
}

3. 同步代码块

更细粒度的控制,可以指定任意对象作为锁。

public void blockMethod() {
    synchronized(this) { // 锁对象可以是任意Object
        // 线程安全代码
    }
}

4. 类锁与对象锁的区别

class DualLockExample {
    // 对象锁
    public synchronized void instanceLock() {}
    
    // 类锁
    public static synchronized void classLock() {}
}

四、synchronized的实现原理

1. JVM层面的实现

synchronized基于Monitor机制实现,每个Java对象都与一个Monitor相关联: - 当线程进入同步块时,会尝试获取Monitor的所有权 - 成功获取后计数器+1(可重入性) - 执行完毕计数器-1,释放锁

2. 锁的升级过程(JDK1.6优化)

3. 对象头Mark Word结构

锁状态信息存储在对象头的Mark Word中:

| 锁状态   | 存储内容                     |
|----------|----------------------------|
| 无锁     | 对象哈希码、分代年龄        |
| 偏向锁   | 线程ID、Epoch、分代年龄     |
| 轻量级锁 | 指向栈中锁记录的指针        |
| 重量级锁 | 指向Monitor的指针           |

五、使用示例

1. 线程安全的单例模式

public class Singleton {
    private static volatile Singleton instance;
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2. 生产者消费者模型

public class Buffer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int CAPACITY = 10;
    
    public synchronized void produce(int item) throws InterruptedException {
        while (queue.size() == CAPACITY) {
            wait();
        }
        queue.add(item);
        notifyAll();
    }
    
    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        int item = queue.poll();
        notifyAll();
        return item;
    }
}

六、注意事项

1. 避免死锁

// 错误的嵌套锁示例
public void transfer(Account from, Account to, int amount) {
    synchronized(from) {
        synchronized(to) { // 可能产生死锁
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

解决方案: - 按固定顺序获取锁 - 使用tryLock()等非阻塞方法

2. 性能考量

3. 不要锁字符串常量

// 危险!字符串常量池具有全局性
synchronized("LOCK") {
    // ...
}

七、与Lock的对比

特性 synchronized Lock
获取方式 自动获取释放 需要手动lock()/unlock()
可中断性 不可中断 支持lockInterruptibly()
公平性 非公平 可配置公平/非公平
条件变量 只能通过wait()/notify() 支持多个Condition
性能 JDK1.6后优化较好 高竞争下可能更好

八、总结

synchronized作为Java内置的同步机制,具有以下特点: 1. 使用简单,JVM自动管理锁的获取和释放 2. 支持可重入,避免自我死锁 3. JDK1.6后性能大幅提升 4. 是解决线程安全问题的首选方案(满足需求时)

对于更复杂的同步需求,可以考虑java.util.concurrent包中的锁工具。在实际开发中,应根据具体场景选择合适的同步策略。

注意:本文基于JDK17编写,不同版本实现细节可能有所差异。 “`

这篇文章共计约1900字,采用Markdown格式编写,包含了代码示例、表格对比等结构化内容,全面覆盖了synchronized关键字的各个方面。如需调整内容或格式,可以进一步修改。

推荐阅读:
  1. 关于@synchronized(self)的用法
  2. Java并发中Synchronized的作用是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:Mysql8中怎么获取JSON字段的值

下一篇:python清洗文件中数据的方法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》