MST
星途 面试题库

面试题:Java并发编程中,锁机制对性能有哪些影响及如何调优?

Java中有多种锁机制,如synchronized关键字、ReentrantLock等。请详细说明不同锁机制在不同并发场景下对性能的影响,例如在高并发读多写少场景、高并发写多读少场景。并举例说明如何通过锁的优化(如锁粗化、锁细化、读写锁的使用等)来提升系统性能。
13.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

synchronized关键字

  1. 高并发读多写少场景
    • 性能影响:synchronized是独占锁,在高并发读多写少场景下,由于每次只能有一个线程获取锁,即使读操作不修改数据,其他读线程也需要等待锁释放,会导致线程频繁竞争锁,性能较低。因为读操作之间不存在数据一致性问题,不需要互斥访问。
    • 示例
public class SynchronizedReadExample {
    private static final Object lock = new Object();
    private int data;
    public int readData() {
        synchronized (lock) {
            return data;
        }
    }
}
  1. 高并发写多读少场景
    • 性能影响:在写多读少场景下,synchronized的独占特性可以保证数据一致性,每次只有一个线程能进行写操作,其他线程无论是读还是写都要等待锁释放。虽然写操作能保证数据正确性,但由于读操作也需要等待写操作完成释放锁,整体性能会受到影响,因为写操作相对耗时,会使读线程等待时间较长。
    • 示例
public class SynchronizedWriteExample {
    private static final Object lock = new Object();
    private int data;
    public void writeData(int newData) {
        synchronized (lock) {
            data = newData;
        }
    }
}

ReentrantLock

  1. 高并发读多写少场景
    • 性能影响:ReentrantLock同样是独占锁,在高并发读多写少场景下,和synchronized类似,读操作线程会频繁竞争锁,因为每次只有一个线程能获取锁,读操作之间不必要的竞争锁会导致性能损耗。
    • 示例
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockReadExample {
    private ReentrantLock lock = new ReentrantLock();
    private int data;
    public int readData() {
        lock.lock();
        try {
            return data;
        } finally {
            lock.unlock();
        }
    }
}
  1. 高并发写多读少场景
    • 性能影响:在写多读少场景下,ReentrantLock能保证写操作的原子性和数据一致性,但是由于它是独占锁,读操作也需要等待写操作完成释放锁,会造成读线程等待,影响系统整体性能。
    • 示例
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockWriteExample {
    private ReentrantLock lock = new ReentrantLock();
    private int data;
    public void writeData(int newData) {
        lock.lock();
        try {
            data = newData;
        } finally {
            lock.unlock();
        }
    }
}

锁优化方式及提升性能说明

  1. 锁粗化
    • 原理:将多个连续的锁操作合并为一个较大范围的锁操作。如果一系列的连续操作都对同一个对象加锁解锁,频繁的加锁解锁操作会带来性能开销,锁粗化就是将这些操作的锁范围扩大,减少加锁解锁次数。
    • 示例
public class LockCoarseningExample {
    private static final Object lock = new Object();
    public void doOperations() {
        // 未锁粗化
        for (int i = 0; i < 10; i++) {
            synchronized (lock) {
                // 一些操作
            }
        }
        // 锁粗化后
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                // 一些操作
            }
        }
    }
}
  • 性能提升:减少了加锁解锁的次数,从而降低了上下文切换的开销,提升性能。
  1. 锁细化
    • 原理:将一个大的锁操作细分为多个小的锁操作,不同部分使用不同的锁,使得多个线程可以同时访问对象的不同部分,提高并发度。
    • 示例:假设有一个包含多个子列表的大列表,如果对整个列表加锁,那么每次只有一个线程能访问列表中的任何子列表。锁细化后,每个子列表可以有自己的锁。
import java.util.ArrayList;
import java.util.List;
public class LockRefinementExample {
    private List<List<Integer>> bigList = new ArrayList<>();
    private List<Lock> subLocks = new ArrayList<>();
    public LockRefinementExample() {
        for (int i = 0; i < 10; i++) {
            bigList.add(new ArrayList<>());
            subLocks.add(new Lock());
        }
    }
    public void addElement(int subListIndex, int element) {
        subLocks.get(subListIndex).lock();
        try {
            bigList.get(subListIndex).add(element);
        } finally {
            subLocks.get(subListIndex).unlock();
        }
    }
}
class Lock {
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException {
        while (isLocked) {
            wait();
        }
        isLocked = true;
    }
    public synchronized void unlock() {
        isLocked = false;
        notify();
    }
}
  • 性能提升:提高了并发访问的能力,不同线程可以同时访问不同子列表,减少了线程等待时间。
  1. 读写锁的使用
    • 原理:读写锁(ReadWriteLock)将锁分为读锁和写锁,允许多个线程同时获取读锁进行读操作(因为读操作不修改数据,不会造成数据一致性问题),而写操作时只允许一个线程获取写锁,并且写锁获取时会排斥其他读锁和写锁。
    • 示例
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private int data;
    public int readData() {
        lock.readLock().lock();
        try {
            return data;
        } finally {
            lock.readLock().unlock();
        }
    }
    public void writeData(int newData) {
        lock.writeLock().lock();
        try {
            data = newData;
        } finally {
            lock.writeLock().unlock();
        }
    }
}
  • 性能提升:在高并发读多写少场景下,极大地提高了性能,因为读操作可以并发执行,只有写操作时才需要独占锁,减少了读操作的等待时间。在写多读少场景下,虽然写操作还是独占,但读操作等待写操作完成后可以并发执行,相比独占锁整体性能也有所提升。