1. 동기화된 잠금 재진입
1.1 소개
동기화된 키워드에는 동기화된 잠금 재진입 기능이 있습니다. 즉, 동기화를 사용할 때 스레드가 객체 잠금을 얻을 때 다음을 수행할 수 있습니다. 객체 잠금을 다시 요청하면 객체 잠금을 다시 가져옵니다. 이는 동기화된 메서드/블록 내에서 이 클래스의 다른 동기화된 메서드/블록을 호출할 때 항상 잠금을 얻을 수 있음을 보여줍니다.
예:
public class Service1 { public synchronized void method1(){ System.out.println("method1"); method2(); } public synchronized void method2(){ System.out.println("method2"); method3(); } public synchronized void method3(){ System.out.println("method3"); } }
public class MyThread extends Thread { @Override public void run(){ Service1 service1 = new Service1(); service1.method1(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
실행 결과는 다음과 같습니다.
☹ 이 결과를 보고 어리둥절했습니다. 재진입 가능한 자물쇠라는 것을 어떻게 증명할 수 있을까요?
➤ "재진입 잠금"의 개념은 자체 내부 잠금을 다시 획득할 수 있다는 것입니다. 예를 들어 스레드가 객체에 대한 잠금을 획득할 때 객체 잠금이 해제되지 않았습니다. 다시 잠그면 잠금이 잠겨 있어도 개체를 계속 얻을 수 있습니다. 잠금을 재진입할 수 없으면 교착 상태가 발생합니다.
➤ "재진입 잠금"의 가장 큰 역할은 교착 상태를 방지하는 것입니다
1.2 분석
프로그램에서 동기화 모니터의 잠금은 명시적으로 해제될 수 없지만, 프로그램 잠금은 다음 상황에서 해제됩니다.
① 현재 스레드의 동기화된 메서드 또는 코드 블록이 실행을 종료할 때 해제됩니다.
② 현재 스레드가 코드 블록 또는 메서드를 종료하기 위해 중단 또는 반환을 만났을 때 해제됩니다.
3 처리되지 않은 오류 또는 예외가 발생하고 비정상적으로 종료됩니다
4 프로그램이 동기화 개체 대기 메서드를 실행하고 현재 스레드가 일시 중지되고 잠금이 해제됩니다
그러면 위 프로그램에서 스레드가 동기화 메서드 method1에 진입하면 다음을 얻습니다. Service1 잠금의 객체이지만, method1 실행 시 동기화 메서드 method2가 호출되는 경우 일반적인 상황에서는 동기화 메서드 method2 실행 시에도 개체 잠금을 획득해야 합니다. 현재 method1의 개체 잠금이 해제되지 않았습니다. 이로 인해 교착 상태가 발생하고 method2를 계속 실행할 수 없습니다. 하지만 위 코드의 실행 결과로 볼 때, method2와 method3은 정상적으로 실행이 가능하다는 뜻인데, 이는 다른 동기식 메서드나 코드 블록 내에서 이 클래스의 코드 블록이나 다른 동기식 메서드를 호출할 때 항상 잠금이 걸렸다는 뜻입니다 .
1.3 부모-자식 상속 가능성
부모-자식 클래스 상속 환경에서는 재진입 잠금이 지원됩니다. 샘플 코드는 다음과 같습니다.public class Service2 { public int i = 10; public synchronized void mainMethod(){ i--; System.out.println("main print i="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Service3 extends Service2 { public synchronized void subMethod(){ try{ while (i>0){ i--; System.out.println("sub print i= "+i); Thread.sleep(100); this.mainMethod(); } }catch (InterruptedException e){ e.printStackTrace(); } } }
public class MyThread extends Thread { @Override public void run(){ Service3 service3 = new Service3(); service3.subMethod(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }실행 결과는 다음과 같습니다.
이 프로그램은 설명합니다. 부모-자식 클래스 상속 관계가 있는 경우 하위 클래스는 "재진입 잠금"을 통해 부모 클래스의 동기화 메서드를 완전히 호출할 수 있습니다.
2. 예외가 발생하면 잠금이 자동으로 해제됩니다.
스레드에서 실행되는 코드에서 예외가 발생하면 해당 스레드가 보유한 잠금이 자동으로 해제됩니다.인증코드는 다음과 같습니다.
public class Service4 { public synchronized void testMethod(){ if(Thread.currentThread().getName().equals("a")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis()); int i=1; while (i == 1){ if((""+Math.random()).substring(0,8).equals("0.123456")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis()); //Integer.parseInt("a"); } } }else{ System.out.println("Thread B run time= "+System.currentTimeMillis()); } } }
public class ThreadA extends Thread{ private Service4 service4; public ThreadA(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
public class ThreadB extends Thread{ private Service4 service4; public ThreadB(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
public class Main { public static void main(String[] args) { try { Service4 service4 = new Service4(); ThreadA a = new ThreadA(service4); a.setName("a"); a.start(); Thread.sleep(500); ThreadB b = new ThreadB(service4); b.setName("b"); b.start(); } catch (InterruptedException e) { e.printStackTrace(); } } }참고로 Service4 클래스의
Integer.parseInt("a");는 현재 Annotation된 상태이며, 실행 결과는 다음과 같습니다.
Since 스레드 a에는 오류가 없지만(true), 현재 스레드 a는 무한 루프에 있고 잠금은 항상 a에 의해 점유되어 스레드 b는 잠금을 얻을 수 없습니다. 즉 스레드 b를 실행할 수 없습니다.
Integer.parseInt("a"); 주석 처리를 해제하면 실행 결과는 다음과 같습니다.
스레드 a에서 오류가 발생하면 스레드 b가 잠금을 획득하여 실행할 수 있습니다. 메소드에서 예외가 발생하면 잠금이 자동으로 해제됩니다.
3. 모든 객체를 모니터로 사용
Java는 "모든 객체"를 "객체 모니터"로 동기화하는 기능을 지원합니다. 이러한 "임의 개체"의 대부분은 인스턴스 변수 및 메서드 매개 변수이며 형식은 동기화된(이 개체 x가 아님) 동기화 코드 블록입니다.샘플 코드는 다음과 같습니다.
public class StringLock { private String lock = "lock"; public void method(){ synchronized (lock){ try { System.out.println("当前线程: "+Thread.currentThread().getName() + "开始"); Thread.sleep(1000); System.out.println("当前线程: "+Thread.currentThread().getName() + "结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final StringLock stringLock = new StringLock(); new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t1").start(); new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t2").start(); } }실행 결과는 다음과 같습니다.
이 개체가 아닌 개체를 잠그면 특정 이점이 있습니다. 클래스에 동기화된 메서드가 많으면 동기화가 가능하더라도 차단됩니다. , 따라서 작업이 영향을 받습니다. 그러나 동기화된 코드 블록을 사용하여 this가 아닌 개체를 잠그면 동기화된(this가 아닌) 코드 블록의 프로그램 및 동기화 방법이 비동기화되고 다른 개체는 이 동기화를 잠급니다. 이 잠금 장치에 대해 경쟁하는 방법이 허용되지 않으므로 운영 효율성이 크게 향상될 수 있습니다.
4. 동기화에는 상속이 없습니다.
동기화 키워드를 추가하지 않고 하위 클래스에 다시 작성하면 상위 클래스의 동기화 메서드가 동기화되지 않으므로 하위 클래스의 메서드에 동기화를 추가해야 합니다. 키워드.
추천 학습: Java 비디오 튜토리얼
위 내용은 동기화의 4가지 기능을 살펴보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!