怎么用Java实现synchronized锁同步机制

发布时间:2021-11-04 13:41:44 作者:柒染
来源:亿速云 阅读:186
# 怎么用Java实现synchronized锁同步机制

## 一、并发编程中的同步问题

### 1.1 为什么需要同步机制

在多线程编程中,当多个线程同时访问共享资源时,如果没有适当的同步机制,可能会导致数据不一致、竞态条件等问题。例如:

```java
public class Counter {
    private int count = 0;
    
    public void increment() {
        count++;  // 这不是原子操作
    }
    
    public int getCount() {
        return count;
    }
}

在这个简单的计数器例子中,count++操作实际上包含三个步骤:读取当前值、增加1、写回新值。如果两个线程同时执行这个操作,可能会导致计数结果不正确。

1.2 同步的基本概念

二、synchronized关键字基础

2.1 synchronized的三种使用方式

2.1.1 同步实例方法

public synchronized void method() {
    // 同步代码块
}

这种形式锁的是当前实例对象(this),同一个实例的多个同步方法会互斥。

2.1.2 同步静态方法

public static synchronized void staticMethod() {
    // 同步代码块
}

这种形式锁的是当前类的Class对象,所有该类的实例调用这个静态方法都会互斥。

2.1.3 同步代码块

public void method() {
    synchronized(obj) {  // obj是锁对象
        // 同步代码块
    }
}

这种形式可以灵活指定锁对象,可以是任意对象实例。

2.2 锁对象与监视器

每个Java对象都有一个内置锁(也称为监视器锁)。当线程进入synchronized方法或代码块时,会自动获取这个锁,退出时自动释放。

三、synchronized的实现原理

3.1 JVM层面的实现

synchronized在JVM中的实现是基于进入和退出Monitor对象来实现的:

3.2 对象头与Mark Word

Java对象在内存中的布局分为三部分:

  1. 对象头(Header)
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

其中对象头包含Mark Word和类型指针。Mark Word是实现synchronized的关键:

|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                   |       State        |
|-------------------------------------------------------|--------------------|
| hashcode:25 | age:4 | biased_lock:1 | lock:2 (01)     |       Normal       |
|-------------------------------------------------------|--------------------|
| thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 (01) |   Biased      |
|-------------------------------------------------------|--------------------|
| ptr_to_lock_record:30 | lock:2 (00)                    | Lightweight Locked |
|-------------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor:30 | lock:2 (10)           | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                               | lock:2 (11) |    Marked    |
|-------------------------------------------------------|--------------------|

3.3 锁升级过程

JDK 1.6之后,synchronized实现了锁升级机制:

  1. 无锁状态:初始状态
  2. 偏向锁:只有一个线程访问同步块时
  3. 轻量级锁:当有多个线程交替进入同步块
  4. 重量级锁:当多线程竞争激烈时

这种优化减少了锁操作的开销。

四、synchronized的高级特性

4.1 可重入性

synchronized是可重入锁,同一个线程可以多次获取同一个锁:

public class ReentrantDemo {
    public synchronized void method1() {
        method2();  // 可以调用另一个同步方法
    }
    
    public synchronized void method2() {
        // ...
    }
}

4.2 等待/通知机制

synchronized配合wait()/notify()/notifyAll()实现线程间通信:

public class WaitNotifyDemo {
    private final Object lock = new Object();
    private boolean condition = false;
    
    public void waitForCondition() throws InterruptedException {
        synchronized(lock) {
            while(!condition) {
                lock.wait();  // 释放锁并等待
            }
            // 条件满足后的处理
        }
    }
    
    public void setCondition() {
        synchronized(lock) {
            condition = true;
            lock.notifyAll();  // 唤醒所有等待线程
        }
    }
}

4.3 死锁问题

synchronized使用不当可能导致死锁:

public class DeadlockDemo {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    public void method1() {
        synchronized(lock1) {
            synchronized(lock2) {
                // ...
            }
        }
    }
    
    public void method2() {
        synchronized(lock2) {
            synchronized(lock1) {
                // ...
            }
        }
    }
}

五、synchronized的性能优化

5.1 减少锁的粒度

将大同步块拆分为小同步块:

// 不推荐
public synchronized void processBigData() {
    // 大量处理逻辑
}

// 推荐
public void processBigData() {
    // 非同步处理
    synchronized(this) {
        // 只同步必要部分
    }
    // 非同步处理
}

5.2 使用不同的锁对象

public class FineGrainedLock {
    private final Object readLock = new Object();
    private final Object writeLock = new Object();
    
    public void read() {
        synchronized(readLock) {
            // 读操作
        }
    }
    
    public void write() {
        synchronized(writeLock) {
            // 写操作
        }
    }
}

5.3 避免锁的长时间持有

public void process() {
    // 耗时的非同步操作
    List<Data> dataList = fetchDataFromDB();
    
    synchronized(this) {
        // 快速更新共享状态
        updateSharedState(dataList);
    }
}

六、synchronized与其他同步机制比较

6.1 synchronized vs ReentrantLock

特性 synchronized ReentrantLock
实现方式 JVM内置实现 JDK代码实现
锁获取方式 自动获取和释放 需要手动lock()和unlock()
可中断 不支持 支持lockInterruptibly()
公平锁 非公平 可选择公平或非公平
条件变量 只能有一个条件 可创建多个Condition
性能 JDK6后优化良好 高竞争下性能更好

6.2 synchronized vs volatile

七、synchronized的最佳实践

7.1 同步对象的选择

// 不推荐 - 锁字符串常量可能有问题
synchronized("LOCK") {
    // ...
}

// 推荐 - 使用私有final对象
private final Object lock = new Object();

public void method() {
    synchronized(lock) {
        // ...
    }
}

7.2 避免同步方法

// 不推荐
public synchronized void process() {
    // ...
}

// 推荐
private final Object lock = new Object();

public void process() {
    synchronized(lock) {
        // ...
    }
}

7.3 双重检查锁定模式

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

注意:必须使用volatile防止指令重排序。

八、常见问题与解决方案

8.1 锁粗化与锁消除

JVM会进行优化:

8.2 锁的公平性问题

synchronized是非公平锁,可能导致线程饥饿。如果需要公平锁,可以使用ReentrantLock。

8.3 避免死锁的策略

  1. 按固定顺序获取锁
  2. 使用tryLock()设置超时
  3. 静态分析工具检测潜在死锁

九、实际案例分析

9.1 线程安全的计数器

public class SafeCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

9.2 生产者-消费者模型

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

十、总结

synchronized是Java中最基本的同步机制,虽然看起来简单,但深入理解其实现原理和最佳实践对于编写高效、安全的并发程序至关重要。随着JDK版本的更新,synchronized的性能已经得到了显著提升,在大多数场景下都是不错的选择。

10.1 synchronized的优势

  1. 使用简单,语法清晰
  2. JVM内置支持,无需额外依赖
  3. JDK6后性能优化良好
  4. 自动释放锁,避免忘记解锁

10.2 适用场景

  1. 简单的同步需求
  2. 锁竞争不激烈的场景
  3. 需要快速实现同步的原型开发

10.3 学习建议

  1. 理解Java内存模型(JMM)
  2. 结合jstack等工具分析锁状态
  3. 阅读JVM源码了解底层实现
  4. 多实践,分析不同场景下的性能表现

通过本文的系统学习,相信读者已经掌握了synchronized的核心概念和使用技巧。在实际开发中,应根据具体场景选择合适的同步策略,平衡性能与正确性的需求。 “`

推荐阅读:
  1. 解决Java Synchronized锁失败问题
  2. Java synchronized锁如何升级jol

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

java synchronized

上一篇:Java中Collection集合的常用方法有哪些

下一篇:怎么实现Android TV 3D卡片无限循环效果

相关阅读

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

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