You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读写锁与synchronized关键字的代码差异及执行时间对比问询

读写锁 vs synchronized:使用差异、实现对比与性能测试

嘿,我来帮你把读写锁和synchronized的区别讲明白,还会给你完整的代码实现和性能测试方案。首先先抓住核心差异,这是理解两者的关键:

核心区别

  • synchronized是独占锁:不管你是读数据还是写数据,同一时间只能有一个线程拿到锁,其他线程都得等着。这种锁简单粗暴,适合读写操作都不多,或者写操作占比很高的场景,但如果是读多写少的情况,就会浪费大量并发读的性能——毕竟读操作本身不会修改数据,完全可以多个线程一起读嘛。
  • 读写锁(ReentrantReadWriteLock)是分离锁:它把读和写的锁分开了:
    • 读锁是共享的:多个线程可以同时获取读锁,一起读数据,互不干扰;
    • 写锁是独占的:只有一个线程能拿到写锁,而且写锁和读锁是互斥的——也就是说,只要有线程在写,其他线程既不能读也不能写;只要有线程在读,写线程就得等着。
      这种设计完美适配读多写少的场景,能大幅提升并发效率。

接下来我先修正你写的读写锁代码(注意你代码里的changaData拼错啦,应该是changeData),然后给出synchronized版本的实现,最后加上性能测试代码,帮你直观看到两者的时间差。

1. 读写锁实现版本

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        NumberWithRWLock number = new NumberWithRWLock(5);
        
        Thread t1 = new Thread(() -> {
            System.out.println("Thread t1 reads: " + number.getData());
            number.changeData(10);
            System.out.println("Thread t1 reads after write: " + number.getData());
        });
        
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread t2 reads: " + number.getData());
            number.changeData(20);
            System.out.println("Thread t2 reads after write: " + number.getData());
        });
        
        t2.start();
        t1.start();
    }
}

class NumberWithRWLock {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock readLock = rwl.readLock();
    private final Lock writeLock = rwl.writeLock();
    int value;

    public NumberWithRWLock(int value) {
        this.value = value;
    }

    public int getData() {
        readLock.lock();
        try {
            // 模拟读操作的耗时,方便后续性能测试看出差异
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            return value;
        } finally {
            // 必须在finally里解锁,防止异常导致锁无法释放
            readLock.unlock();
        }
    }

    public int changeData(int change) {
        writeLock.lock();
        try {
            // 模拟写操作的耗时
            try { Thread.sleep(50); } catch (InterruptedException e) {}
            value = change;
            return value;
        } finally {
            writeLock.unlock();
        }
    }
}

2. synchronized实现版本

public class SynchronizedDemo {
    public static void main(String[] args) {
        NumberWithSync number = new NumberWithSync(5);
        
        Thread t1 = new Thread(() -> {
            System.out.println("Thread t1 reads: " + number.getData());
            number.changeData(10);
            System.out.println("Thread t1 reads after write: " + number.getData());
        });
        
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread t2 reads: " + number.getData());
            number.changeData(20);
            System.out.println("Thread t2 reads after write: " + number.getData());
        });
        
        t2.start();
        t1.start();
    }
}

class NumberWithSync {
    int value;

    public NumberWithSync(int value) {
        this.value = value;
    }

    // 用synchronized修饰方法,相当于给当前对象加锁
    public synchronized int getData() {
        // 和读写锁版本用相同的模拟耗时,保证测试公平
        try { Thread.sleep(10); } catch (InterruptedException e) {}
        return value;
    }

    public synchronized int changeData(int change) {
        try { Thread.sleep(50); } catch (InterruptedException e) {}
        value = change;
        return value;
    }
}

3. 性能对比测试

单个线程的测试看不出明显差异,我们来模拟读多写少的真实场景:100个读线程,每个读100次;10个写线程,每个写10次。用CountDownLatch等待所有线程完成,统计总耗时。

import java.util.concurrent.CountDownLatch;

public class PerformanceTest {
    private static final int READ_THREAD_COUNT = 100;
    private static final int WRITE_THREAD_COUNT = 10;

    public static void main(String[] args) throws InterruptedException {
        // 先测读写锁版本
        long rwLockTotalTime = testReadWriteLock();
        // 再测synchronized版本
        long syncTotalTime = testSynchronized();

        System.out.println("\n=== 性能对比结果 ===");
        System.out.println("读写锁总耗时: " + rwLockTotalTime + " ms");
        System.out.println("synchronized总耗时: " + syncTotalTime + " ms");
        System.out.println("读写锁比synchronized快了: " + (syncTotalTime - rwLockTotalTime) + " ms");
    }

    private static long testReadWriteLock() throws InterruptedException {
        NumberWithRWLock number = new NumberWithRWLock(0);
        CountDownLatch latch = new CountDownLatch(READ_THREAD_COUNT + WRITE_THREAD_COUNT);
        long startTime = System.currentTimeMillis();

        // 启动所有读线程
        for (int i = 0; i < READ_THREAD_COUNT; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    number.getData();
                }
                latch.countDown();
            }).start();
        }

        // 启动所有写线程
        for (int i = 0; i < WRITE_THREAD_COUNT; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    number.changeData(j);
                }
                latch.countDown();
            }).start();
        }

        // 等待所有线程执行完毕
        latch.await();
        return System.currentTimeMillis() - startTime;
    }

    private static long testSynchronized() throws InterruptedException {
        NumberWithSync number = new NumberWithSync(0);
        CountDownLatch latch = new CountDownLatch(READ_THREAD_COUNT + WRITE_THREAD_COUNT);
        long startTime = System.currentTimeMillis();

        // 启动所有读线程
        for (int i = 0; i < READ_THREAD_COUNT; i++) {
            new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    number.getData();
                }
                latch.countDown();
            }).start();
        }

        // 启动所有写线程
        for (int i = 0; i < WRITE_THREAD_COUNT; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    number.changeData(j);
                }
                latch.countDown();
            }).start();
        }

        latch.await();
        return System.currentTimeMillis() - startTime;
    }
}

测试结果说明

你运行上面的测试代码后,会发现:

  • 在读多写少的场景下,读写锁的耗时会比synchronized少很多——因为100个读线程可以同时执行读操作,而synchronized每次只能一个线程执行,不管是读还是写。
  • 如果是写多读少或者读写频率相近的场景,两者的性能差异会很小,甚至synchronized可能略优,因为读写锁有额外的锁调度开销。

适用场景总结

  • 用synchronized:读写操作频率差不多,或者写操作占比高;想要代码简洁,不需要分离读写锁的场景。
  • 用读写锁:读操作远多于写操作的场景(比如缓存读取、配置读取),能充分利用并发读的优势提升性能。

内容的提问来源于stack exchange,提问作者jakubradzikowski97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:10:37