Lock은 최상위 인터페이스이며 모든 메서드는 아래와 같습니다.
해당 하위 클래스 목록은 다음과 같습니다.
일반적으로 ReentrantLock을 사용하여 정의합니다. 이들 사이의 관계는 아래 그림에 나와 있습니다.
PS: Sync는 동기화 잠금을 의미하고 FairSync는 공정한 잠금을 의미하며 NonfairSync는 불공정 잠금을 의미합니다.
모든 기술 학습은 사용에서부터 시작되므로 우리도 예외는 아닙니다. 먼저 ReentrantLock의 기본 사용을 살펴보겠습니다.
public class LockExample { // 创建锁对象 private final ReentrantLock lock = new ReentrantLock(); public void method() { // 加锁操作 lock.lock(); try { // 业务代码...... } finally { // 释放锁 lock.unlock(); } } }
ReentrantLock을 만든 후 두 가지 핵심 사항이 있습니다. 작업:
잠금 작업: lock()
잠금 해제 작업: Unlock()
많은 사람들이 생각할 것입니다(특히 초보 친구의 경우). ReentrantLock의 기본 구현은 공정한 잠금이지만 이는 사실이 아닙니다. ReentrantLock은 기본적으로 불공정한 잠금입니다(주로 성능 고려 사항 때문입니다).
예를 들어 다음 코드는
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }
The 위 프로그램의 실행 결과는 다음과 같습니다.
위의 실행 결과를 보면 ReentrantLock이 기본적으로 불공정 잠금인 것을 알 수 있습니다. 스레드의 이름은 생성된 순서대로 증가하므로 공정한 잠금이라면 스레드의 실행도 순서대로 증가해야 한다. 스레드 인쇄 순서가 잘못되었습니다. 설명 ReentrantLock은 기본적으로 불공정 잠금입니다.
ReentrantLock을 공정한 잠금으로 설정하는 것도 매우 간단합니다. ReentrantLock을 생성할 때 실제 구성 매개변수만 설정하면 됩니다. 다음 코드에서 볼 수 있듯이:
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象(公平锁) private static final ReentrantLock lock = new ReentrantLock(true); public static void main(String[] args) { // 定义线程任务 Runnable runnable = new Runnable() { @Override public void run() { // 加锁 lock.lock(); try { // 打印执行线程的名字 System.out.println("线程:" + Thread.currentThread().getName()); } finally { // 释放锁 lock.unlock(); } } }; // 创建多个线程 for (int i = 0; i < 10; i++) { new Thread(runnable).start(); } } }
위 프로그램의 실행 결과는 다음과 같습니다.
위 결과에서 볼 수 있듯이 ReentrantLock에 대한 실제 구성 매개변수를 명시적으로 설정하면 ReentrantLock이 공정한 잠금이 되고 스레드가 잠금을 획득하는 순서가 질서정연해집니다.
실제로 ReentrantLock의 소스 코드를 보면 공정한 잠금인지 불공정한 잠금인지 확인할 수 있습니다. ReentrantLock의 소스 코드 중 일부는 다음과 같이 구현됩니다.
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
위의 소스 코드에서 ReentrantLock은 기본적으로 불공평한 잠금을 생성합니다. 생성 시 구성 매개변수의 값이 명시적으로 true로 설정되면 공정한 잠금이 생성됩니다.
ReentrantLock을 사용할 때 잠금을 해제해야 합니다. 그렇지 않으면 잠금이 항상 점유되고 잠금을 사용하는 다른 스레드가 영원히 기다리게 되므로 ReentrantLock을 사용합니다. 이때 반드시 마지막으로 잠금을 해제하여 잠금이 해제되는지 확인하시기 바랍니다.
카운터 예시
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); System.out.println("Hello,ReentrantLock."); // 此处会报异常,导致锁不能正常释放 int number = 1 / 0; // 释放锁 lock.unlock(); System.out.println("锁释放成功!"); } }
위 프로그램의 실행 결과는 다음과 같습니다.
위 결과를 보면 Exception이 발생하면 정상적으로 Lock이 해제되지 않는 것을 알 수 있는데, 이는 잠금을 사용하는 다른 스레드가 영구적으로 대기 상태에 있게 됩니다.긍정적인 예
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); try { System.out.println("Hello,ReentrantLock."); // 此处会报异常 int number = 1 / 0; } finally { // 释放锁 lock.unlock(); System.out.println("锁释放成功!"); } } }
위 프로그램의 실행 결과는 다음과 같습니다.
위 결과를 보면, 메소드에서 예외가 발생하더라도 프로그램에 영향을 미치지 않음을 알 수 있습니다. ReentrantLock 잠금 작업을 해제하여 이 잠금을 사용하는 다른 스레드가 정상적으로 획득하고 실행할 수 있도록 합니다. 3. 잠금은 여러 번 해제할 수 없습니다.잠금 작업 횟수와 잠금 해제 작업 횟수는 일대일로 일치해야 하며, 잠금을 여러 번 해제할 수 없습니다. 이렇게 하면 프로그램에서 오류가 보고되기 때문입니다.카운터 예시
하나의 잠금은 두 개의 잠금 해제 작업에 해당하며, 이로 인해 프로그램이 오류를 보고하고 실행을 종료하게 됩니다. 샘플 코드는 다음과 같습니다.
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // 加锁操作 lock.lock(); // 第一次释放锁 try { System.out.println("执行业务 1~"); // 业务代码 1...... } finally { // 释放锁 lock.unlock(); System.out.println("锁释锁"); } // 第二次释放锁 try { System.out.println("执行业务 2~"); // 业务代码 2...... } finally { // 释放锁 lock.unlock(); System.out.println("锁释锁"); } // 最后的打印操作 System.out.println("程序执行完成."); } }
위 프로그램의 실행 결과는 다음과 같습니다. :
위의 결과를 보면, 두 번째 Unlock이 실행되면 프로그램에서 오류를 보고하고 실행을 종료하여 예외 이후의 코드가 정상적으로 실행되지 않는 것을 알 수 있습니다.在使用 ReentrantLock 时,需要注意不要将加锁操作放在 try 代码中,这样会导致未加锁成功就执行了释放锁的操作,从而导致程序执行异常。
反例
import java.util.concurrent.locks.ReentrantLock; public class LockExample { // 创建锁对象 private static final ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { try { // 此处异常 int num = 1 / 0; // 加锁操作 lock.lock(); } finally { // 释放锁 lock.unlock(); System.out.println("锁释锁"); } System.out.println("程序执行完成."); } }
以上程序的执行结果如下:
从上述结果可以看出,如果将加锁操作放在 try 代码中,可能会导致两个问题:
未加锁成功就执行了释放锁的操作,从而导致了新的异常;
释放锁的异常会覆盖程序原有的异常,从而增加了排查问题的难度。
위 내용은 Java에서 ReentrantLock의 일반적인 함정은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!