synchronized关键字
- 高并发读多写少场景
- 性能影响:synchronized是独占锁,在高并发读多写少场景下,由于每次只能有一个线程获取锁,即使读操作不修改数据,其他读线程也需要等待锁释放,会导致线程频繁竞争锁,性能较低。因为读操作之间不存在数据一致性问题,不需要互斥访问。
- 示例:
public class SynchronizedReadExample {
private static final Object lock = new Object();
private int data;
public int readData() {
synchronized (lock) {
return data;
}
}
}
- 高并发写多读少场景
- 性能影响:在写多读少场景下,synchronized的独占特性可以保证数据一致性,每次只有一个线程能进行写操作,其他线程无论是读还是写都要等待锁释放。虽然写操作能保证数据正确性,但由于读操作也需要等待写操作完成释放锁,整体性能会受到影响,因为写操作相对耗时,会使读线程等待时间较长。
- 示例:
public class SynchronizedWriteExample {
private static final Object lock = new Object();
private int data;
public void writeData(int newData) {
synchronized (lock) {
data = newData;
}
}
}
ReentrantLock
- 高并发读多写少场景
- 性能影响: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();
}
}
}
- 高并发写多读少场景
- 性能影响:在写多读少场景下,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();
}
}
}
锁优化方式及提升性能说明
- 锁粗化
- 原理:将多个连续的锁操作合并为一个较大范围的锁操作。如果一系列的连续操作都对同一个对象加锁解锁,频繁的加锁解锁操作会带来性能开销,锁粗化就是将这些操作的锁范围扩大,减少加锁解锁次数。
- 示例:
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++) {
// 一些操作
}
}
}
}
- 性能提升:减少了加锁解锁的次数,从而降低了上下文切换的开销,提升性能。
- 锁细化
- 原理:将一个大的锁操作细分为多个小的锁操作,不同部分使用不同的锁,使得多个线程可以同时访问对象的不同部分,提高并发度。
- 示例:假设有一个包含多个子列表的大列表,如果对整个列表加锁,那么每次只有一个线程能访问列表中的任何子列表。锁细化后,每个子列表可以有自己的锁。
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();
}
}
- 性能提升:提高了并发访问的能力,不同线程可以同时访问不同子列表,减少了线程等待时间。
- 读写锁的使用
- 原理:读写锁(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();
}
}
}
- 性能提升:在高并发读多写少场景下,极大地提高了性能,因为读操作可以并发执行,只有写操作时才需要独占锁,减少了读操作的等待时间。在写多读少场景下,虽然写操作还是独占,但读操作等待写操作完成后可以并发执行,相比独占锁整体性能也有所提升。