减少锁持有的时间
对于使用锁来进行并发控制的程序而言,线程对锁的持有时间越长,必能会导致锁竞争变的越激烈。
可以想象下我们排队买包子,买包子的窗口只有那么几个,如果轮到我们了我们才去想要买什么包子,势必会影响整个队伍的效率,所以我们应该在之前就想好自己要买什么包子。
public synchronized void syncFunc(){
func1();
mutexFunc();
func2();
}
如果以上代码的func1和func2都是比较耗时的方法,但是他不需要同步,只有中间的方法需要同步,如果我们这么写势必会造成整个方法持有锁的时间,时间长了会导致竞争概率的提高,白白浪费系统资源,影响程序性能,所以只对有需要的方法加lock。
public void syncFunc(){
func1();
synchronized(lock) {
mutexFunc();
}
func2();
}
较少锁的粒度
ConcurrentHashMap就是一个很好的例子,比如其put方法发生Hash冲突时他只是对该Node进行加锁操作。
读写分离
读操作不会影响数据的属性和结构,所以我们对于读-读线程间就不需要同步,来减少锁的竞争。
锁分离
LinkedBlockingQueue 就是一个很好的例子,我们知道其是链表实现的,主要的操作有take和put,并且其分别在head和tail进行操作,之间是没有影响的,所以其使用两把锁分别来控制其take和put操作。
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
粗化锁
for () {
synchronized(lock) { // 本身锁的申请和释放都是有代价的,所以我们要进行粗化
//code
}
}
synchronized(lock) {
for () {
// 本身锁的申请和释放都是有代价的,所以我们要进行粗化
//code
}
}