当前位置: 首页 > news >正文

同步锁:同步实现的几种方式

Java中同步实现的主要几种方式,以及不同的同步锁

在 Java 中,同步是多线程编程中非常重要的概念,它确保了多个线程可以安全地访问共享资源,避免数据不一致和竞态条件。Java 提供了多种同步实现的方式,并且每种方式背后都有不同的同步锁。下面将详细介绍 Java 中同步实现的几种方式,以及 不同的同步锁

一、Java 中同步实现的主要几种方式

  1. 使用 synchronized 关键字(方法同步和代码块同步)
  2. 使用 ReentrantLock(显式锁)
  3. 使用 ReadWriteLock(读写锁)
  4. 使用 volatile 关键字
  5. 使用 Semaphore 和 CountDownLatch(并发工具类)

二、synchronized 关键字

synchronized 是 Java 中实现同步的最基本方式。它可以用于方法或者代码块的同步,确保同一时刻只有一个线程可以访问同步代码块或方法。

1. 方法同步

通过 synchronized 修饰实例方法或者静态方法,确保同一时刻只有一个线程可以执行该方法。

  • 实例方法同步:锁是对象本身。
  • 静态方法同步:锁是类的 Class 对象。
示例代码:
class Counter {
    private int count = 0;

    // 使用 synchronized 修饰实例方法
    public synchronized void increment() {
        count++;
    }

    // 使用 synchronized 修饰静态方法
    public static synchronized void staticIncrement() {
        // 对类的静态资源进行同步
    }
}
2. 代码块同步

synchronized 还可以修饰代码块,在方法内部控制代码的同步。在这种方式下,我们可以指定一个对象作为锁,从而只锁住特定的代码块。

示例代码:
class Counter {
    private int count = 0;

    public void increment() {
        synchronized(this) { // 锁定当前对象
            count++;
        }
    }
}
说明:
  • 优点:简单直观,Java 内置支持。
  • 缺点:不灵活,无法指定多个锁对象;会导致性能瓶颈(锁的粒度较大)。

三、ReentrantLock(显式锁)

ReentrantLock 是 Java java.util.concurrent.locks 包中的一个显式锁,它比 synchronized 更加灵活。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(); // 释放锁
        }
    }
}
说明:
  • 优点:
    • 可以精细地控制锁的获取和释放。
    • 提供了 tryLock()lockInterruptibly() 等方法,支持锁的中断操作。
    • 可以通过 ReentrantLock 实现公平锁。
  • 缺点:
    • 需要手动释放锁,否则可能会导致死锁。

四、ReadWriteLock(读写锁)

ReadWriteLockjava.util.concurrent.locks 包中的一个接口,它为多线程访问资源提供了更精细的控制。它有两个主要的锁:读锁写锁

  • 读锁:多个线程可以同时持有读锁,允许并发读取。
  • 写锁:一个线程持有写锁时,其他线程不能读取也不能写入。
示例代码:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class Counter {
    private int count = 0;
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

    // 读操作:使用读锁
    public int getCount() {
        rwLock.readLock().lock(); // 获取读锁
        try {
            return count;
        } finally {
            rwLock.readLock().unlock(); // 释放读锁
        }
    }

    // 写操作:使用写锁
    public void increment() {
        rwLock.writeLock().lock(); // 获取写锁
        try {
            count++;
        } finally {
            rwLock.writeLock().unlock(); // 释放写锁
        }
    }
}
说明:
  • 优点:
    • 适用于读操作远多于写操作的场景,能够提高并发性能。
    • 写锁互斥,保证数据一致性;读锁并发,提升读性能。
  • 缺点:
    • 实现复杂,读写锁的竞争较激烈时可能导致性能下降。

五、volatile 关键字

volatile 关键字是 Java 中的一个轻量级同步机制,确保变量的值在多个线程之间保持一致。对于标记为 volatile 的变量,每次读取都会从主内存中读取,而不是从线程的本地缓存中读取。

示例代码:
class Counter {
    private volatile int count = 0;

    public void increment() {
        count++;  // 因为 count 是 volatile,这里可以保证最新的值被读取和写入
    }
}
说明:
  • 优点:
    • synchronized 更轻量级,不涉及锁的管理,性能较好。
  • 缺点:
    • 只能确保单个变量的可见性,并不能保证复合操作(如 count++)的原子性。

六、并发工具类:SemaphoreCountDownLatch

除了显式锁之外,Java 还提供了许多并发工具类来帮助实现同步和线程间的协调。

1. Semaphore

Semaphore 是一个计数信号量,它用于限制某些资源的并发访问数量。

示例代码:
import java.util.concurrent.Semaphore;

class Counter {
    private final Semaphore semaphore = new Semaphore(3);  // 限制最大并发数为3

    public void accessResource() throws InterruptedException {
        semaphore.acquire();  // 获取信号量
        try {
            // 访问共享资源的代码
            System.out.println(Thread.currentThread().getName() + " accessing resource");
        } finally {
            semaphore.release();  // 释放信号量
        }
    }
}
2. CountDownLatch

CountDownLatch 是一个同步辅助工具,它允许一个或多个线程等待其他线程完成某些操作后再继续执行。通常用于多个线程并行工作,最后一起等待结束。

示例代码:
import java.util.concurrent.CountDownLatch;

class Counter {
    private final CountDownLatch latch = new CountDownLatch(1);

    public void waitForFinish() throws InterruptedException {
        latch.await();  // 等待
        System.out.println("Task is finished.");
    }

    public void finishTask() {
        latch.countDown();  // 完成任务
        System.out.println("Task finished.");
    }
}

七、不同同步锁的对比

锁类型优点缺点适用场景
synchronized简单易用,Java 内置支持性能差,锁粒度大,不能灵活控制简单的线程同步,确保线程互斥
ReentrantLock更灵活,支持可中断、定时锁、尝试锁等高级功能需要手动释放锁,容易引发死锁需要灵活控制的复杂并发场景
ReadWriteLock读操作多时性能较高,适合读写分离的场景写操作竞争时性能差读多写少的场景,数据库缓存等
volatile轻量级,性能好,适用于单一变量的共享只保证可见性,不保证原子性单一变量的共享,不能用于复杂操作
Semaphore限制并发数,控制线程访问资源的数量不能保证线程的执行顺序控制访问数量,限制资源访问并发数
CountDownLatch线程协调,等待其他线程完成任务后再继续执行只能使用一次,不能重用需要线程等待并发任务完成的场景

八、总结

Java 提供了多种同步实现方式,不同的同步机制适用于不同的应用场景。选择合适的同步方式可以提高多线程程序的并发性和性能。

相关文章:

  • NFC拉起微信小程序申请URL scheme 汇总
  • 一文掌握ADSL拨号代理的搭建方法,及详细使用
  • 如何证明有限域的大小都是2的幂次
  • 千峰React:Hooks(上)
  • 利用 Windows Terminal 和 SSH Config 简化 Linux 服务器管理
  • postgresql postgis扩展相关
  • VSCode 使用import导入js/vue等时添加智能提示,并可跳转到定义
  • 改进YOLOv8模型的空间注意力机制研究:RFAConv的贡献与实现
  • PostgreSQL的备份方式
  • SQL注入(order by、limit),seacmsv9联合注入数据
  • ROS2中的图形化显示---visualization_msgs
  • 【缓冲区】数据库备份的衍生问题,缓冲区在哪里?JVMor操作系统?(二)
  • 大模型应用案例 | 大模型+金融运维,擎创携手某证券创新运维能力新范式
  • 力扣-动态规划-70 爬楼梯
  • 山东大学计算机组成与设计第六章习题解析
  • C++11 智能指针:unique_ptr、shared_ptr和weak_ptr 功能特性 模拟实现
  • 【新手入门】SQL注入之盲注
  • 双机热备旁挂组网场景实验
  • A64指令集基本指令(一):分支指令
  • Apache Doris:一款高性能的实时数据仓库
  • 多家外资看好中国市场!野村建议“战术超配”,花旗上调恒指目标价
  • 中华人民共和国和巴西联邦共和国关于强化携手构建更公正世界和更可持续星球的中巴命运共同体,共同维护多边主义的联合声明
  • 尊严的代价:新加坡福利体系下的价值困境
  • 深一度|在亚马尔的天才面前,姆巴佩戴上“帽子”又如何
  • 泰特现代美术馆25年:那些瞬间,让艺术面向所有人
  • 工行回应两售出金条疑似有杂质:情况不属实,疑似杂质应为金条售出后的外部附着物