Java多线程实现之同步方法详解

 更新时间:2025年12月11日 10:52:09   作者:AA-代码批发V哥  
当多个线程同时访问共享资源时,可能会导致数据不一致等线程安全问题,Java提供了synchronized关键字来实现线程同步,确保同一时刻只有一个线程可以访问共享资源,下面就来介绍一下如何使用,感兴趣的可以了解一下

当多个线程同时访问共享资源时,可能会导致数据不一致等线程安全问题,Java提供了synchronized关键字来实现线程同步,确保同一时刻只有一个线程可以访问共享资源。本文我将详细介绍Java同步方法的原理、使用方式以及相关的最佳实践,帮你更好地理解和应用线程同步技术。

一、线程安全问题

1.1 竞态条件(Race Condition)

当多个线程同时访问和操作共享资源,并且最终结果取决于线程执行的顺序时,就会产生竞态条件。例如:

class Counter {
    private int count = 0;
    
    public void increment() {
        count++; // 非原子操作,可能导致竞态条件
    }
    
    public int getCount() {
        return count;
    }
}

1.2 原子性问题

count++操作实际上包含三个步骤:读取、修改、写入。在多线程环境下,这些步骤可能被中断,导致数据不一致。

二、synchronized关键字

2.1 同步方法

使用synchronized修饰的方法称为同步方法,同一时刻只有一个线程可以执行该方法:

class Counter {
    private int count = 0;
    
    public synchronized void increment() {
        count++; // 线程安全的操作
    }
    
    public synchronized int getCount() {
        return count;
    }
}

2.2 同步代码块

使用synchronized修饰的代码块称为同步代码块,可以更细粒度地控制同步范围:

class Counter {
    private int count = 0;
    private final Object lock = new Object(); // 锁对象
    
    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
    
    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

2.3 静态同步方法

使用synchronized修饰的静态方法,锁定的是类的Class对象:

class MyClass {
    private static int staticCount = 0;
    
    public static synchronized void incrementStatic() {
        staticCount++;
    }
}

三、同步方法的原理

3.1 内置锁(Intrinsic Lock)

每个Java对象都有一个内置锁(也称为监视器锁),synchronized方法和代码块就是基于这个内置锁实现的。

3.2 锁的获取与释放

  • 当线程进入synchronized方法或代码块时,会自动获取锁
  • 当线程退出synchronized方法或代码块时,会自动释放锁
  • 如果锁已被其他线程持有,则当前线程会被阻塞,进入等待状态

四、同步方法的使用场景

4.1 原子操作

确保对共享资源的操作是原子的,例如计数器、累加器等:

class BankAccount {
    private double balance;
    
    public synchronized void deposit(double amount) {
        balance += amount;
    }
    
    public synchronized void withdraw(double amount) {
        balance -= amount;
    }
}

4.2 状态一致性

确保对象的状态在多线程环境下保持一致:

class DataContainer {
    private String data;
    private boolean isReady = false;
    
    public synchronized void setData(String data) {
        this.data = data;
        isReady = true;
        notifyAll(); // 通知等待的线程
    }
    
    public synchronized String getData() throws InterruptedException {
        while (!isReady) {
            wait(); // 等待数据准备好
        }
        return data;
    }
}

五、同步方法的注意事项

5.1 性能开销

同步操作会带来一定的性能开销,因为涉及到锁的获取和释放。应尽量缩小同步范围,避免不必要的同步。

5.2 死锁风险

当多个线程互相等待对方释放锁时,可能会导致死锁:

public class DeadlockExample {
    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) {
                // 执行操作
            }
        }
    }
}

5.3 锁的粒度

  • 粗粒度锁:同步范围大,线程竞争激烈,性能低
  • 细粒度锁:同步范围小,线程竞争少,性能高
  • 应根据实际情况选择合适的锁粒度

六、替代同步方法的技术

6.1 ReentrantLock

java.util.concurrent.locks.ReentrantLock提供了比synchronized更灵活的锁机制:

import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

6.2 原子类

java.util.concurrent.atomic包提供了原子类,用于实现无锁的原子操作:

import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet(); // 原子操作
    }
}

七、同步方法的最佳实践

7.1 最小化同步范围

只对关键代码块进行同步,避免整个方法都被同步:

public void process() {
    // 非关键代码
    
    synchronized (this) {
        // 关键代码,需要同步
    }
    
    // 非关键代码
}

7.2 使用专用锁对象

使用专门的锁对象,而不是使用this或类的Class对象:

private final Object lock = new Object();

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

7.3 优先使用JDK提供的并发工具

优先使用java.util.concurrent包中的并发工具,如ConcurrentHashMapCopyOnWriteArrayList等,它们内部已经实现了线程安全。

总结

Java的synchronized关键字是实现线程同步的基本方法,通过内置锁机制确保同一时刻只有一个线程可以访问共享资源,实际开发中我们应根据具体情况选择合适的同步方式,避免线程安全问题,同时也要注意性能开销和死锁风险。

到此这篇关于Java多线程实现之同步方法详解的文章就介绍到这了,更多相关Java多线程同步内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA git版本回滚使用解读

    IDEA git版本回滚使用解读

    这篇文章主要介绍了Git的分区和回滚操作,包括工作区、暂存区和版本库的概念,以及如何在不同状态下进行回滚操作,如未commit、commit未push、已commit但未push以及已commit并push的情况
    2025-10-10
  • Java中常见的并发控制手段浅析

    Java中常见的并发控制手段浅析

    所谓并发控制就是帮助我们程序员更容易的让线程之间合作,让线程之间相互配合来满足业务逻辑,这篇文章主要给大家介绍了关于Java中常见的并发控制手段的相关资料,需要的朋友可以参考下
    2021-08-08
  • 详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    详细总结Java堆栈内存、堆外内存、零拷贝浅析与代码实现

    零拷贝,这是个耳熟能详的名词,是开发岗面试中经常提及的问题.最近在回顾Netty的基础原理,还是把NIO中关于堆外内存的知识点过了一遍,这里就针对堆栈内存 堆外内存和零拷贝这几个概念以及相关知识做一下记录,需要的朋友可以参考下
    2021-05-05
  • 解决@RequestBody搭配@Data的大坑

    解决@RequestBody搭配@Data的大坑

    这篇文章主要介绍了解决@RequestBody搭配@Data的大坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java日常练习题,每天进步一点点(51)

    Java日常练习题,每天进步一点点(51)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-08-08
  • github上的java项目怎么运行(面向小白)

    github上的java项目怎么运行(面向小白)

    这篇文章主要介绍了github上的java项目怎么运行(面向小白),今天从github把我以前写的一个小demo下载下来了,第一次下载项目,摸索了一个多小时,才运行起来,需要的朋友可以参考下
    2019-06-06
  • Mybatis执行SQL时多了一个limit的问题及解决方法

    Mybatis执行SQL时多了一个limit的问题及解决方法

    这篇文章主要介绍了Mybatis执行SQL时多了一个limit的问题及解决方法,Mybatis拦截器方法识别到配置中参数supportMethodsArguments 为ture时会分页处理,本文结合示例代码给大家讲解的非常详细,需要的朋友可以参考下
    2022-10-10
  • Java实战之酒店人事管理系统的实现

    Java实战之酒店人事管理系统的实现

    这篇文章主要介绍了如何用Java实现酒店人事管理系统,文中采用的技术有:JSP、Spring、SpringMVC、MyBatis等,感兴趣的小伙伴可以学习一下
    2022-03-03
  • Eclipse中配置Maven的图文教程

    Eclipse中配置Maven的图文教程

    这篇文章主要介绍了Eclipse中配置Maven的图文教程,需要的朋友可以参考下
    2020-12-12
  • Java for循环倒序输出的操作代码

    Java for循环倒序输出的操作代码

    在Java中,要实现一个for循环的倒序输出,通常我们会使用数组或集合(如ArrayList)作为数据源,然后通过倒序遍历这个数组或集合来实现,这篇文章主要介绍了Java for循环倒序输出,需要的朋友可以参考下
    2024-07-07

最新评论