锁优化
About 3 min
在实际的应用中,如果能够在代码的应用层面上进行合理的锁优化,也是会提升系统性能的。这里我们就展开讲讲锁的优化思路。
减少锁持有时间
减少锁的持有时间,其实就是移出没有必要加锁的操作。想想看如果每个人(线程)占了餐馆的位置,并在上面吃饭、打游戏(费时操作),如果是就餐高峰期。每个人占着位置1个多小时,那么就会造成其他很多人没办法就餐。模拟程序如下
Person
class Person {
void action() throws InterruptedException {
synchronized (Person.class) {
eat();
playGame();
}
}
void eat() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + " finish eating, time: " + LocalDateTime.now());
}
void playGame() throws InterruptedException {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName() + " finish gaming, time: " + LocalDateTime.now());
}
}
RestaurantMain
public class RestaurantMain {
public static void main(String[] args) throws InterruptedException {
System.out.println("Start: " + LocalDateTime.now());
var threads = new ArrayList<Thread>();
for (int i = 0; i < 5; i++) {
var thread = new Thread(() -> {
try {
new Person().action();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
threads.add(thread);
}
for (Thread thread : threads) thread.start();
for (Thread thread : threads) thread.join();
System.out.println("End: " + LocalDateTime.now());
}
}
每个人占着位子,需要完成吃饭和打完游戏才离开:用时55秒
Start: 2023-09-12T18:51:36.788911
Thread-0 finish eating, time: 2023-09-12T18:51:37.791840
Thread-0 finish gaming, time: 2023-09-12T18:51:47.808546
Thread-3 finish eating, time: 2023-09-12T18:51:48.813600
Thread-3 finish gaming, time: 2023-09-12T18:51:58.815262
Thread-2 finish eating, time: 2023-09-12T18:51:59.815981
Thread-2 finish gaming, time: 2023-09-12T18:52:09.818383
Thread-4 finish eating, time: 2023-09-12T18:52:10.822304
Thread-4 finish gaming, time: 2023-09-12T18:52:20.825164
Thread-1 finish eating, time: 2023-09-12T18:52:21.826791
Thread-1 finish gaming, time: 2023-09-12T18:52:31.832319
End: 2023-09-12T18:52:31.832695
如果修改上述Person类的action方法如下:
void action() throws InterruptedException {
synchronized (Person.class) {
eat();
}
playGame();
}
真实的业务场景,就是你吃完饭就快些离开释放位子,打游戏不用占着位子: 用时15秒
Start: 2023-09-12T18:55:43.016838
Thread-0 finish eating, time: 2023-09-12T18:55:44.019018
Thread-1 finish eating, time: 2023-09-12T18:55:45.029491
Thread-2 finish eating, time: 2023-09-12T18:55:46.030756
Thread-3 finish eating, time: 2023-09-12T18:55:47.032526
Thread-4 finish eating, time: 2023-09-12T18:55:48.036353
Thread-0 finish gaming, time: 2023-09-12T18:55:54.028820
Thread-1 finish gaming, time: 2023-09-12T18:55:55.026498
Thread-2 finish gaming, time: 2023-09-12T18:55:56.029223
Thread-3 finish gaming, time: 2023-09-12T18:55:57.030282
Thread-4 finish gaming, time: 2023-09-12T18:55:58.037243
End: 2023-09-12T18:55:58.037563
减少锁粒度
锁分离
锁粗化
通常情况下,为了保证多线程的有效并发,会要求每个线程持有锁的时间尽量的短。即在使用完公共资源后,应该立即释放锁。只有这样,等待这个锁的其他线程才能尽早地获得资源执行任务。但是如果对同一个锁不停地进行请求、同步和释放,加上任务运行时间较短,这样消耗系统宝贵的资源,反而不利于性能。
优化前
public class Test1 {
private final static int concurrency = 100;
private final static int times = 100000;
private static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
var threads = new Thread[concurrency];
for (int i = 0; i < concurrency; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < times; j++) {
synchronized (IncrementMain.class) {
count++;
}
}
});
}
long start = System.currentTimeMillis();
for (Thread thread : threads) thread.start();
for (Thread thread : threads) thread.join();
long end = System.currentTimeMillis();
System.out.printf("result: %d, time: %d\n", count, end - start);
}
}
优化后
public class Test2 {
private final static int concurrency = 100;
private final static int times = 100000;
private static volatile int count = 0;
public static void main(String[] args) throws InterruptedException {
var threads = new Thread[concurrency];
for (int i = 0; i < concurrency; i++) {
threads[i] = new Thread(() -> {
synchronized (IncrementMain.class) {
for (int j = 0; j < times; j++) {
count++;
}
}
});
}
long start = System.currentTimeMillis();
for (Thread thread : threads) thread.start();
for (Thread thread : threads) thread.join();
long end = System.currentTimeMillis();
System.out.printf("result: %d, time: %d\n", count, end - start);
}
}
优化前618
毫秒,优化后72
毫秒。而且随着times
增长,这个差距会越来越大。
Info
对于大量出现的短时任务,可以考虑采用锁粗化来进行性能优化。