Отсканируйте QR-код ниже или WeChat, чтобы найти официальную учетную запись.
菜鸟飞呀飞
, вы можете следить за публичной учетной записью WeChat, читать дальшеSpring源码分析
а такжеJava并发编程
статья.
1. Вопросы
- В предыдущей статье реализация честных и нечестных блокировок была представлена в сочетании с исходным кодом [ссылка на статью]. В этой статье мы сравним их с точки зрения справедливости и производительности.
- Прежде чем читать эту статью, ответьте на следующие два вопроса.
-
1.
Являются ли несправедливые блокировки обязательно несправедливыми? -
2.
У кого лучше производительность между честной блокировкой и нечестной блокировкой?
2. Контраст
- Справедливая блокировка и нечестная блокировка сравниваются в основном по двум аспектам честности и производительности.
2.1 Справедливость
- В заключении предыдущей статьи было упомянуто, что справедливые блокировки и нечестные блокировки — это справедливость двух с момента ожидания потока для получения блокировки. В справедливой блокировке, когда несколько потоков захватывают блокировку, поток, который получает блокировку, должен быть потоком с наибольшим временем ожидания в очереди синхронизации. При несправедливых блокировках, когда несколько потоков захватывают блокировку, поток, который получает блокировку, не обязательно является потоком, ожидающим дольше всего в очереди синхронизации.Может случиться так, что поток вне очереди синхронизации захватит блокировку первым.
- Для честных блокировок процесс получения блокировок потоком можно представить следующей схематической диаграммой (картинка взята из публичного аккаунта:
Mr羽墨青衫
, ссылка на статью:Углубленный анализ принципа реализации реентерабельной блокировки Java ReentrantLock).
-
Из рисунка видно, что когда поток T1, удерживающий блокировку, освобождает блокировку, он пробуждает поток T2 в очереди синхронизации Пока в очереди синхронизации есть потоки, ожидающие получения блокировки, другие люди, которые только что вошел и хочет получить блокировку, не может поставить в очередь, включая то, что T1 хочет получить блокировку сам, также должен стоять в очереди, что гарантирует, что поток с наибольшим временем ожидания может получить блокировку, что обеспечивает справедливость.
-
Для недобросовестных блокировок схематическая диаграмма получения блокировки потоком может быть представлена следующей схемой. (Картинка взята из публичного аккаунта:
Mr羽墨青衫
, ссылка на статью:Углубленный анализ принципа реализации реентерабельной блокировки Java ReentrantLock).
- Из рисунка видно, что когда поток, удерживающий блокировку Т1, освобождает блокировку, он разбудит поток Т2 в очереди синхронизации.В это время, даже если в очереди синхронизации есть потоки, только что пришедший поток извне хочет получить блокировку В это время возможна прямая конкуренция за блокировку, включая сам поток T1 В это время T2, T1 и поток, который только что пришел извне, будут бороться за блокировку Блокировка вместе. Хотя поток T2 ожидает дольше всего в это время, не гарантируется, что T2 захватит блокировку. , поэтому в настоящее время это несправедливо.
- В начале статьи был задан вопрос,
非公平锁一定不公平吗?
Ответ не обязательно, для ситуации, только что показанной на рисунке выше, несправедливая блокировка в настоящее время является несправедливой. Однако есть особый случай, вы можете обратиться к следующей диаграмме. Например, после того, как поток T1 снимает блокировку, нет ни одного потока вне очереди синхронизации AQS, чтобы конкурировать за блокировку.После того, как поток T1 снимает блокировку, ему не нужно запрашивать блокировку.Тот, который просыпается, - это T2, и теперь есть поток T2, чтобы захватить блокировку, поэтому поток, который может получить блокировку в это время, должен быть T2. В этом случае Несправедливая блокировка снова справедлива, потому что даже если в это время есть очередь синхронизации T3, T4, ..., Tn потоков, но T2 ждет дольше всего, поэтому именно T2 получает блокировку (принцип, что очередь синхронизации AQS следует FIFO).
- На схематической диаграмме несправедливых блокировок мы также можем обнаружить, что если потоку не удается получить блокировку и он попадает в очередь синхронизации AQS, он всегда будет стоять в очереди до тех пор, пока другие потоки, получившие блокировку, не разбудят его или пока он не будет прерван другими потоками. , Команда. То есть: один раз в очереди, всегда в очереди.
представление
- Производительность честных и нечестных блокировок различна, и производительность недобросовестных блокировок будет лучше, чем у честных блокировок. Зачем? Поскольку, когда справедливая блокировка получает блокировку, поток с наибольшим временем ожидания всегда получает блокировку, поэтому, когда поток T1 освобождает блокировку, если он хочет продолжить получение блокировки, он также должен стоять в очереди в конце очередь синхронизации, что приведет к частому переключению потоков в контексте, и чем больше потоков, тем серьезнее будет потребление ЦП.
- Хотя производительность недобросовестных блокировок выше, чем у справедливых блокировок,
饥饿
Случай. В худшем случае может существовать поток, который никогда не получит блокировку. Однако с точки зрения производительности,饥饿
На проблему можно пока не обращать внимания, что может быть одной из причин, по которой ReentrantLock по умолчанию создает несправедливые блокировки. - Давайте возьмем демонстрацию в качестве примера, чтобы сравнить производительность честных и нечестных блокировок.
public class Demo {
// 公平锁
private static Lock fairLock = new ReentrantLock(true);
// 非公平锁
private static Lock nonFairLock = new ReentrantLock(false);
// 计数器
private static int fairCount = 0;
// 计数器
private static int nonFairCount = 0;
public static void main(String[] args) throws InterruptedException {
System.out.println("公平锁耗时: " + testFairLock(10));
System.out.println("非公平锁耗时: " + testNonFairLock(10));
System.out.println("公平锁累加结果: " + fairCount);
System.out.println("非公平锁累加结果: " + nonFairCount);
}
public static long testFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 创建threadNum个线程,让其以公平锁的方式,对fairCount进行自增操作
List<Thread> fairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
fairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
fairLock.lock();
fairCount++;
fairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : fairList) {
thread.start();
}
// 让所有线程执行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static long testNonFairLock(int threadNum) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
// 创建threadNum个线程,让其以非公平锁的方式,对nonFairCountCount进行自增操作
List<Thread> nonFairList = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
nonFairList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
nonFairLock.lock();
nonFairCount++;
nonFairLock.unlock();
}
countDownLatch.countDown();
}));
}
long startTime = System.currentTimeMillis();
for (Thread thread : nonFairList) {
thread.start();
}
// 让所有线程执行完
countDownLatch.await();
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
}
- В приведенной выше демонстрации создан
threadNum
темы, то пусть этоthreadNum
Каждый поток одновременно накапливает 10 000 операций над переменными, использует честные и нечестные блокировки для обеспечения безопасности потоков и, наконец, подсчитывает трудоемкие результаты честных и нечестных блокировок. - когда
threadNum = 10
При трехкратном повторении теста результаты следующие.
частота | честный замок | несправедливый замок |
---|---|---|
1 | 618ms | 22ms |
2 | 544ms | 20ms |
3 | 569ms | 15ms |
- когда
threadNum = 20
При трехкратном повторении теста результаты следующие.
частота | честный замок | несправедливый замок |
---|---|---|
1 | 1208ms | 25ms |
2 | 1146ms | 26ms |
3 | 1215ms | 19ms |
- когда
threadNum = 30
При трехкратном повторении теста результаты следующие.
частота | честный замок | несправедливый замок |
---|---|---|
1 | 1595ms | 28ms |
2 | 1543ms | 31ms |
3 | 1601ms | 31ms |
- Тестовая среда: macOS 10.14.6, процессор: 2,9 ГГц, Intel Core i7, память: 16 ГБ, 2133 МГц
- Из приведенных выше результатов испытаний можно сделать вывод, что,
非公平锁的耗时远远小于公平锁的耗时
, что означает, что в случае одновременных недобросовестных блокировок性能更好,吞吐量更大
. Разница более заметна, когда количество потоков выше.
связанное предложение
- Мониторы: краеугольный камень параллельного программирования
- Первое понимание принципа реализации CAS
- Интерпретация исходного кода класса Unsafe и сценариев использования
- Принцип разработки синхронизатора очередей (AQS)
- Анализ исходного кода Queue Synchronizer (AQS)
- Повторная блокировка (ReentrantLock) Анализ исходного кода
- Посмотрите на процесс создания Bean через исходный код
- Процесс запуска контейнера из серии исходных кодов Spring
- Весной самое то! наиболее! наиболее! Важный постпроцессор! никто из них! ! !
- @Импорт и @EnableXXX
- Напишите подключаемый модуль, который интегрирует Redis и Spring.
- Почему динамический прокси JDK должен быть реализован на основе интерфейсов, а не на основе наследования?
- FactoryBean — одна из точек расширения Spring.
- Принцип реализации аннотации @Autowired
- Практическое применение шаблона разработки стратегии
- Как Spring разрешает циклические зависимости