предисловие
Сегодня мы продолжаем изучать параллелизм. Прежде чем мы изучили JMM, мы знаем, что в параллельном программировании для обеспечения безопасности потоков нам необходимо обеспечить атомарность, видимость и упорядоченность потоков. Среди них часто появляется синхронизированный, потому что он не только обеспечивает атомарность, но также обеспечивает видимость и порядок. Почему, потому что synchronized — это блокировка. С помощью блокировок задачи, которые изначально были параллельными, можно сделать последовательными. Однако, как видите, это также привело к серьезному снижению производительности. Поэтому в крайнем случае не используйте блокировки, особенно для WEB-серверов с особо высокими требованиями к пропускной способности. Если заблокировано, производительность упадет в геометрической прогрессии.
Но нам по-прежнему нужны блокировки, и по-прежнему нужны блокировки для обеспечения точности данных в момент тех или иных операций над общими переменными. В мире Java есть 3 блокировки, сегодня мы в основном поговорим об использовании этих 3-х замков.
- синхронизированное ключевое слово
- ReentrantLock блокировка повторного входа
- ReadWriteLock Блокировка чтения-записи
1. синхронизированное ключевое слово
Можно сказать, что синхронизация — это первое ключевое слово, которое мы изучаем при изучении параллелизма. Это ключевое слово является грубым и эффективным и обычно используется младшими программистами. Поэтому оно часто приводит к некоторым потерям производительности и проблемам взаимоблокировок.
Вот 3 варианта использования синхронизированного:
void resource1() {
synchronized ("resource1") {
System.out.println("作用在同步块中");
}
}
synchronized void resource3() {
System.out.println("作用在实例方法上");
}
static synchronized void resource2() {
System.out.println("作用在静态方法上");
}
Организуйте использование следующих ключевых слов:
- Укажите объект блокировки (кодовый блок): заблокируйте данный объект и получите блокировку данного объекта перед вводом кода синхронизации.
- Воздействие непосредственно на метод экземпляра: это эквивалентно блокировке текущего экземпляра, и блокировка текущего экземпляра должна быть получена до ввода кода синхронизации.
- Прямое воздействие на статические методы: эквивалентно блокировке текущего класса, перед входом в блок синхронизированного кода необходимо получить блокировку текущего класса.
Synchronized снимает блокировку при возникновении исключения, требующего внимания.
Синхронизированный измененный код будет иметь инструкции monitorenter и monitorexit при создании байт-кода, и эти две инструкции вызывают две из восьми основных инструкций виртуальной машины на нижнем уровне — блокировку и разблокировку.
Синхронизированный хоть и всемогущ, но все же имеет много ограничений, например, при его использовании часто возникают взаимоблокировки, которые невозможно обработать, поэтому в Java в версии 1.5 был добавлен еще один интерфейс Lock Lock. Давайте посмотрим, что находится под интерфейсом.
2. Блокировка повторного входа
JDK добавил пакет java.util.concurrent в версии 1.5, который был написан мастером параллелизма Дугом Ли, и его код потрясающий. Стоит усердно учиться, включая Замок, о котором мы говорили сегодня.
Блокировка интерфейса
/**
* @since 1.5
* @author Doug Lea
*/
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
аннулировать блокировку (); получить блокировку
void lockInterruptibly() ;
boolean tryLock(); Попытаться получить блокировку, если нет, немедленно вернуть false.
логическое значение tryLock (долгое время, блок TimeUnit) в
void unlock(); Подождать блокировку в течение заданного времени и автоматически сдаться, если время превышено
Условие newCondition(); Получите хорошего партнера для повторных блокировок, используйте с повторными блокировками
Институциональный метод абстракции Lock упоминался выше, так что же представляет собой реализация Lock? Стандарт реализует ReentrantLock, ReadWriteLock. То есть реентерабельные блокировки и блокировки чтения-записи, о которых мы сегодня говорим. Сначала поговорим о повторных блокировках.
Начнем с простого примера:
package cn.think.in.java.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockText implements Runnable {
/**
* Re - entrant - Lock
* 重入锁,表示在单个线程内,这个锁可以反复进入,也就是说,一个线程可以连续两次获得同一把锁。
* 如果你不允许重入,将导致死锁。注意,lock 和 unlock 次数一定要相同,如果不同,就会导致死锁和监视器异常。
*
* synchronized 只有2种情况:1继续执行,2保持等待。
*/
static Lock lock = new ReentrantLock();
static int i;
public static void main(String[] args) throws InterruptedException {
LockText lockText = new LockText();
Thread t1 = new Thread(lockText);
Thread t2 = new Thread(lockText);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
lock.lock();
try {
i++;
} finally {
// 因为lock 如果发生了异常,是不会释放锁的,所以必须在 finally 块中释放锁
// synchronized 发生异常会主动释放锁
lock.unlock();
}
}
}
}
В приведенном выше коде мы использовали операцию в блоке try, которая защищает критический ресурс i. Можно видеть, что блокировка с повторным входом отображается независимо от того, открыта ли блокировка или снята.Одна вещь, которую следует отметить, это то, что если возникает исключение, когда работает блокировка с повторным входом, она не освобождает блокировку, как при синхронизации, поэтому блокировку необходимо отпустили наконец.. В противном случае произойдет взаимоблокировка.
Что такое реентерабельная блокировка? Блокировка есть блокировка, почему она называется реентерабельной блокировкой? Причина, по которой он так называется, заключается в том, что этот тип блокировки можно вводить многократно (поток).Давайте посмотрим на следующий код:
lock.lock();
lock.lock();
tyr{
i++;
} finally{
lock.unlock();
lock.unlock();
}
В этом случае поток получает две блокировки дважды подряд, что разрешено. Если эта операция не разрешена, то во второй раз, когда тот же поток получит блокировку, он заблокируется сам с собой. Конечно, следует отметить, что если вы получаете блокировку несколько раз, вы должны снять блокировку столько же раз.Если вы снимаете блокировку слишком много раз, вы получите исключение IllegalMonitorStateException.Наоборот, если вы снимаете блокировку реже, если Эквивалент этого потока не снял блокировку, другие потоки не смогут войти в критическую секцию.
Реентерабельные блокировки могут реализовать все функции синхронизированных, причем функции более мощные Давайте посмотрим, какие функции доступны.
Прерывание ответа
Для синхронизированного, если поток ожидает блокировки, есть только два результата: либо он получает блокировку и продолжает работать, либо продолжает ждать. Третьей возможности нет, тогда если у меня есть требование: мне нужно, чтобы поток прерывал поток во время ожидания, а синхронизированный не может этого сделать. Реентерабельная блокировка может это сделать, это метод lockInterruptably, который может получить блокировку и поддерживать прерывание потока в процессе получения блокировки, то есть, если метод прерывания потока будет вызван, будет выброшено исключение. Является ли он более мощным, чем метод блокировки? Напишем пример:
package cn.think.in.java.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock(重入锁)
*
* Condition(条件)
*
* ReadWriteLock(读写锁)
*/
public class IntLock implements Runnable {
/**
* 默认是不公平的锁,设置为 true 为公平锁
*
* 公平:在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程;
* 使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢)
* 还要注意的是,未定时的 tryLock 方法并没有使用公平设置
*
* 不公平:此锁将无法保证任何特定访问顺序
*
* 拾遗:1 该类的序列化与内置锁的行为方式相同:一个反序列化的锁处于解除锁定状态,不管它被序列化时的状态是怎样的。
* 2.此锁最多支持同一个线程发起的 2147483648 个递归锁。试图超过此限制会导致由锁方法抛出的 Error。
*/
static ReentrantLock lock1 = new ReentrantLock(true);
static ReentrantLock lock2 = new ReentrantLock();
int lock;
/**
* 控制加锁顺序,方便制造死锁
* @param lock
*/
public IntLock(int lock) {
this.lock = lock;
}
/**
* lockInterruptibly 方法: 获得锁,但优先响应中断
* tryLock 尝试获得锁,不等待
* tryLock(long time , TimeUnit unit) 尝试获得锁,等待给定的时间
*/
@Override
public void run() {
try {
if (lock == 1) {
// 如果当前线程未被中断,则获取锁。
lock1.lockInterruptibly();// 即在等待锁的过程中,可以响应中断。
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 试图获取 lock 2 的锁
lock2.lockInterruptibly();
} else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 该线程在企图获取 lock1 的时候,会死锁,但被调用了 thread.interrupt 方法,导致中断。中断会放弃锁。
lock1.lockInterruptibly();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
// 查询当前线程是否保持此锁。
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ": 线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
/**
* 这部分代码主要是针对 lockInterruptibly 方法,该方法在线程发生死锁的时候可以中断线程。让线程放弃锁。
* 而 synchronized 是没有这个功能的, 他要么获得锁继续执行,要么继续等待锁。
*/
IntLock r1 = new IntLock(1);
IntLock r2 = new IntLock(2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
Thread.sleep(1000);
// 中断其中一个线程(只有线程在等待锁的过程中才有效)
// 如果线程已经拿到了锁,中断是不起任何作用的。
// 注意:这点 synchronized 是不能实现此功能的,synchronized 在等待过程中无法中断
t2.interrupt();
// t2 线程中断,抛出异常,并放开锁。没有完成任务
// t1 顺利完成任务。
}
}
В приведенном выше коде мы запускаем два потока отдельно и создаем взаимоблокировку.Если он синхронизирован, взаимоблокировка не может быть снята.В это время выходит мощность реентерабельной блокировки.Мы вызываем метод прерывания потока, чтобы прервать поток. , мы сказали, что этот метод будет вызывать исключения, когда потоки спят, присоединяются, ждут, а здесь также овца. Из-за используемого нами метода блокировки lockInterruptably этот метод похож на то, что мы только что сказали, при ожидании lock, Если поток прерван, возникает исключение и вызывается метод finally unlock. Обратите внимание, что мы используем isHeldByCurrentThread в finally, чтобы определить, удерживает ли текущий поток блокировку. Это мера предосторожности, и размещающий поток не удерживайте эту блокировку, что приводит к исключению monitorState.
заблокировать приложение
Помимо ожидания уведомления, есть еще один способ избежать взаимоблокировки — дождаться тайм-аута, если это время превышено, поток откажется от получения блокировки, что не поддерживается синхронизацией. Итак, как его использовать?
package cn.think.in.java.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeLock implements Runnable {
static ReentrantLock lock = new ReentrantLock(false);
@Override
public void run() {
try {
// 最多等待5秒,超过5秒返回false,若获得锁,则返回true
if (lock.tryLock(5, TimeUnit.SECONDS)) {
// 锁住 6 秒,让下一个线程无法获取锁
System.out.println("锁住 6 秒,让下一个线程无法获取锁");
Thread.sleep(6000);
} else {
System.out.println("get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public static void main(String[] args) {
TimeLock tl = new TimeLock();
Thread t1 = new Thread(tl);
Thread t2 = new Thread(tl);
t1.start();
t2.start();
}
}
В приведенном выше коде мы устанавливаем время ожидания блокировки равным 5 секундам, но в блоке синхронизации мы устанавливаем 6-секундную паузу, а поток вне блокировки ждет 5 раз и обнаруживает, что он все еще не может получить блокировку. замок, и будет сдаваться. Используйте логику else и завершите выполнение. Обратите внимание, что здесь мы по-прежнему выносим суждения в блоке finally. Если мы не выносим суждений, возникает исключение IllegalMonitorStateException.
Конечно, метод tryLock также может не иметь временного параметра, если блокировку получить невозможно, он немедленно вернет false, иначе вернет true. Этот метод также является хорошим способом борьбы с взаимоблокировками. Напишем пример:
package cn.think.in.java.lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLock implements Runnable {
static ReentrantLock lock1 = new ReentrantLock();
static ReentrantLock lock2 = new ReentrantLock();
int lock;
public TryLock(int lock) {
this.lock = lock;
}
@Override
public void run() {
// 线程1
if (lock == 1) {
while (true) {
// 获取1的锁
if (lock1.tryLock()) {
try {
// 尝试获取2的锁
if (lock2.tryLock()) {
try {
System.out.println(Thread.currentThread().getId() + " : My Job done");
return;
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
}
} else {
// 线程2
while (true) {
// 获取2的锁
if (lock2.tryLock()) {
try {
// 尝试获取1的锁
if (lock1.tryLock()) {
try {
System.out.println(Thread.currentThread().getId() + ": My Job done");
return;
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
}
}
}
/**
* 这段代码如果使用 synchronized 肯定会引起死锁,但是由于使用 tryLock,他会不断的尝试, 当第一次失败了,他会放弃,然后执行完毕,并释放外层的锁,这个时候就是
* 另一个线程抢锁的好时机。
* @param args
*/
public static void main(String[] args) {
TryLock r1 = new TryLock(1);
TryLock r2 = new TryLock(2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
Если этот код использует синхронизированный, это определенно вызовет взаимоблокировку, но из-за использования tryLock он будет продолжать попытки, когда это не удастся в первый раз, он сдастся, а затем завершит выполнение и снимет внешнюю блокировку, на этот раз еще одно удачное время для захвата блокировок потоками.
Честные и нечестные замки
В большинстве случаев блокировки неэффективны. Когда система выбирает блокировки, это происходит случайным образом и не будет следовать определенному порядку, например хронологическому порядку.Основной особенностью справедливых блокировок является то, что они не вызовут голодания. Пока вы стоите в очереди, вы все равно можете получить ресурсы в конечном итоге. Если мы используем synchronized , получаемая нами блокировка будет несправедливой. Следовательно, это также мощное преимущество реентерабельных блокировок по сравнению с синхронизированными. Также напишем пример:
package cn.think.in.java.lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairLock implements Runnable {
// 公平锁和非公平锁的结果完全不同
/*
* 10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
10 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
9 获得锁
======================下面是公平锁,上面是非公平锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得锁
9 获得锁
10 获得
*
* */
static ReentrantLock unFairLock = new ReentrantLock(false);
static ReentrantLock fairLock = new ReentrantLock(true);
@Override
public void run() {
while (true) {
try {
fairLock.lock();
System.out.println(Thread.currentThread().getId() + " 获得锁");
} finally {
fairLock.unlock();
}
}
}
/**
* 默认是不公平的锁,设置为 true 为公平锁
*
* 公平:在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程;
* 使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢)
* 还要注意的是,未定时的 tryLock 方法并没有使用公平设置
*
* 不公平:此锁将无法保证任何特定访问顺序,但是效率很高
*
*/
public static void main(String[] args) {
FairLock fairLock = new FairLock();
Thread t1 = new Thread(fairLock, "cxs - t1");
Thread t2 = new Thread(fairLock, "cxs - t2");
t1.start();
t2.start();
}
}
Конструктор реентерабельной блокировки имеет логический параметр, ture означает честный, false означает несправедливый, значение по умолчанию — несправедливый, а честные блокировки снижают производительность. Из результатов работы кода видно, что порядок печати честных блокировок полностью попеременно запускается, а порядок нечестных блокировок полностью случайный. Примечание. Если нет особых требований, не используйте честную блокировку, это значительно снизит пропускную способность.
Подытожим здесь преимущества реентерабельных блокировок по сравнению с синхронизированными:
- Вы можете прервать поток, пока он ожидает блокировки, что-то синхронизированное не может.
- Вы можете попытаться получить блокировку, отказаться, если не можете ее получить, или установить определенное время, что также невозможно для синхронизации.
- Можно установить справедливую блокировку, синхронизированная — это нечестная блокировка по умолчанию, а справедливая блокировка не может быть реализована.
Конечно, все скажут, что synchronized может взаимодействовать между потоками через методы ожидания и уведомления Object, а могут ли реентерабельные блокировки это делать? Хозяин сказал всем, конечно можно! Очередь блокировки в JDK реализована с повторной блокировкой и ее партнерским условием.
Хороший партнер для повторных блокировок ----- Состояние
Вы помните, что в начале интерфейса Lock есть метод newCondition, этот метод предназначен для получения условия. Условие привязано к блокировке. Каковы методы состояния? Посмотрим:
public interface Condition {
void await() throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
void awaitUninterruptibly();
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
Смотря, является ли это специальным атрибутом, Condition использует метод await, чтобы не конфликтовать с методом ожидания класса Object, а метод signal соответствует методу notify. Метод signalAll соответствует методу notifyAll. Существуют также некоторые ограниченные по времени методы ожидания, которые имеют тот же эффект, что и метод ожидания Object. Обратите внимание, что существует метод awaitUninterruptably, который, как следует из названия, не реагирует на прерывание потока, в отличие от метода ожидания Object. Метод awaitUntil должен ждать до заданного абсолютного времени. Если сигнал не вызван или не прерван. Как это использовать? Возьмем кусок кода:
package cn.think.in.java.lock.condition;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 重入锁的好搭档
*
* await 使当前线程等待,同时释放当前锁,当其他线程中使用 signal 或者 signalAll 方法时,线程会重新获得锁并继续执行。
* 或者当线程被中断时,也能跳出等待,这和 Object.wait 方法很相似。
* awaitUninterruptibly() 方法与 await 方法基本相同,但是它并不会在等待过程中响应中断。
* singal() 该方法用于唤醒一个在等待中的线程,相对的 singalAll 方法会唤醒所有在等待的线程,这和 Object.notify 方法很类似。
*/
public class ConditionTest implements Runnable {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
@Override
public void run() {
try {
lock.lock();
// 该线程会释放 lock 的锁,也就是说,一个线程想调用 condition 的方法,必须先获取 lock 的锁。
// 否则就会像 object 的 wait 方法一样,监视器异常
condition.await();
System.out.println("Thread is going on");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ConditionTest t = new ConditionTest();
Thread t1 = new Thread(t);
t1.start();
Thread.sleep(1000);
// 通知 t1 继续执行
// main 线程必须获取 lock 的锁,才能调用 condition 的方法。否则就是监视器异常,这点和 object 的 wait 方法是一样的。
lock.lock(); // IllegalMonitorStateException
// 从 condition 的等待队列中,唤醒一个线程。
condition.signal();
lock.unlock();
}
}
Можно сказать, что использование условия очень похоже на использование метода ожидания класса Object.Независимо от того, какой поток вызывает метод ожидания или сигнала, соответствующая блокировка должна быть получена, иначе возникнет исключение IllegalMonitorStateException.
На данный момент мы можем сказать, что реализация Condition сильнее, чем ожидание и уведомление Object, которые включают ожидание до указанного абсолютного времени, и существует метод awaitUninterruptably, на который не влияет прерывание потока. Поэтому мы говорим: используйте реентерабельные блокировки всякий раз, когда можете, и старайтесь не использовать бессмысленные синхронизированные . Хотя синхронизация оптимизирована после JDK 1.6, по-прежнему рекомендуется использовать повторные блокировки.
3. Блокировка чтения и записи
Великий Дуг Ли создал не только реентерабельные блокировки, но и блокировки чтения-записи. Что такое блокировка чтения-записи? Мы знаем, что причина небезопасности потоков связана с изменением данных несколькими потоками.Если вы не изменяете данные, вам вообще не нужны блокировки. Мы можем полностью разделить чтение и запись для повышения производительности, не использовать блокировки при чтении и добавлять блокировки при записи. Так устроен ReadWriteLock.
Итак, как его использовать?
package cn.think.in.java.lock;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
static Lock lock = new ReentrantLock();
static ReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
static Lock readLock = reentrantReadWriteLock.readLock();
static Lock writeLock = reentrantReadWriteLock.writeLock();
int value;
public Object handleRead(Lock lock) throws InterruptedException {
try {
lock.lock();
// 模拟读操作,读操作的耗时越多,读写锁的优势就越明显
Thread.sleep(1000);
return value;
} finally {
lock.unlock();
}
}
public void handleWrite(Lock lock, int index) throws InterruptedException {
try {
lock.lock();
Thread.sleep(1000); // 模拟写操作
value = index;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
final ReadWriteLockDemo demo = new ReadWriteLockDemo();
Runnable readRunnable = new Runnable() {
@Override
public void run() {
try {
demo.handleRead(readLock);
// demo.handleRead(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable writeRunnable = new Runnable() {
@Override
public void run() {
try {
demo.handleWrite(writeLock, new Random().nextInt());
// demo.handleWrite(lock, new Random().nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
/**
* 使用读写锁,这段程序只需要2秒左右
* 使用普通的锁,这段程序需要20秒左右。
*/
for (int i = 0; i < 18; i++) {
new Thread(readRunnable).start();
}
for (int i = 18; i < 20; i++) {
new Thread(writeRunnable).start();
}
}
}
Используйте метод readLock() класса ReentrantReadWriteLock, чтобы вернуть блокировку чтения, и writeLock, чтобы вернуть блокировку записи. Для тестирования мы используем обычные реентерабельные блокировки и блокировки чтения-записи. Как тестировать?
Два цикла: один цикл открывает 18 потоков для чтения данных, а один цикл открывает два потока для записи. Если вы используете обычную блокировку с повторным входом, это займет 20 секунд, поскольку обычная блокировка с повторным входом при чтении все еще является последовательной. А если использовать блокировку чтение-запись, то всего 2 секунды, то есть запись серийная. Чтение выполняется параллельно, что значительно повышает производительность.
Примечание. Когда дело доходит до письма, оно последовательное. Например, операции чтения и записи, операции записи и записи являются последовательными, а параллельными являются только операции чтения и чтения.
Блокировка чтения-записи Интерфейс ReadWriteLock имеет всего 2 метода:
Блокировка readLock(); возвращает блокировку чтения Блокировка writeLock(); возвращает блокировку записи
Его стандартным классом реализации является класс ReentrantReadWriteLock, который, как и обычные реентерабельные блокировки, также может реализовывать справедливые блокировки, реакцию на прерывание, приложение блокировки и другие функции. Поскольку блокировка чтения или блокировка записи, которую они возвращают, реализует интерфейс Lock.
Суммировать
На этом этапе мы разъяснили использование трех блокировок в мире Java.Из процесса анализа мы знаем, что повторно входимая блокировка JDK 1.5 может полностью заменить ключевое слово synchronized и может выполнять многие функции, которые не синхронизированы. Например, ответ на прерывание, приложение блокировки, справедливая блокировка и т. д., а также партнер повторной блокировки, условие, также сильнее, чем ожидание и уведомление объекта. Например, есть ожидание с настройкой абсолютного времени и метод ожидания. который игнорирует прерывание потока.Все они синхронизированы и не могут быть реализованы. Существуют также блокировки чтения-записи, оптимизирующие производительность чтения, которые полностью параллельны при чтении.В некоторых сценариях, таких как много операций чтения и мало операций записи, производительность будет улучшена на геометрическом уровне.
Поэтому в будущем не используйте синхронизацию, если можете, и если вы не используете ее правильно, это приведет к взаимоблокировке.
Сегодняшние Java три замка на введение здесь.
удачи! ! ! !