먼저 질문 내용을 살펴보겠습니다.
(학습 영상 공유: java 영상 튜토리얼)
public class TestSync2 implements Runnable { int b = 100; synchronized void m1() throws InterruptedException { b = 1000; Thread.sleep(500); //6 System.out.println("b=" + b); } synchronized void m2() throws InterruptedException { Thread.sleep(250); //5 b = 2000; } public static void main(String[] args) throws InterruptedException { TestSync2 tt = new TestSync2(); Thread t = new Thread(tt); //1 t.start(); //2 tt.m2(); //3 System.out.println("main thread b=" + tt.b); //4 } @Override public void run() { try { m1(); } catch (InterruptedException e) { e.printStackTrace(); } } }
이 프로그램의 출력 결과는?
프로그램 출력 결과
main thread b=2000 b=1000 或 main thread b=1000 b=1000
지식 포인트 검사
인스턴스 잠금을 동기화합니다.
동시성에서 메모리 가시성.
(더 관련 있는 면접 질문 추천 : Java 면접 질문 및 답변)
Java에서는 멀티스레드 프로그램이 가장 이해하기 어렵고 디버그하기 어렵고 실행 결과가 생각한 대로 실행되지 않는 경우가 많습니다. 그래서 자바에서의 멀티스레딩은 특히나 어려웠던 기억이 있는데, 대학에서 C 언어 레벨 2 시험을 볼 때, 최종 출력 결과에 대해 묻는 질문이 ++ 등 여러 가지 우선순위였다는 것을 어렴풋이 기억합니다. 다음 질문 중 일부를 살펴보세요. 연산자 우선순위 및 연관성 문제. 그냥 외우기만 하면 되지만, 자바 멀티스레딩은 아직 잘 이해해야 하고, 외워도 소용이 없습니다.
다음은 간단한 분석으로 시작됩니다.
이 질문에는 2개의 스레드(메인 스레드, 하위 스레드)가 관련되어 있으며 키워드에는 동기화 및 Thread.sleep이 포함됩니다.
Synchronized 키워드는 여전히 매우 복잡하며(때때로 제대로 이해되지 않을 수도 있으므로 위의 질문도 약간 오해될 수 있음), 해당 기능은 스레드 동기화를 달성하는 것입니다(스레드 동기화를 달성하는 방법은 여러 가지가 있지만 단지 방법일 뿐입니다). 이에 대해서는 후속 기사에서 논의할 예정이며 마스터 Doug Lea의 일부 구현을 연구해야 합니다.) 그 작업은 한 번에 하나의 스레드만 동기화된 블록에 들어갈 수 있도록 동기화해야 하는 코드를 잠그는 것입니다(실제로는 비관적입니다). 전략) 스레드가 보안만 기억하는지 확인합니다.
일반 키워드 동기화 사용
잠금 객체 지정: 주어진 객체를 잠그고, 동기화 코드를 입력하기 전에 주어진 객체의 잠금을 활성화해야 합니다. 인스턴스 메서드에 직접 적용: 현재 인스턴스를 잠그는 것과 동일합니다. 동기화 코드를 입력하기 전에 현재 인스턴스의 잠금을 얻어야 합니다. 정적 메서드에 직접 작용: 현재 클래스를 잠그는 것과 동일합니다. 동기화 코드를 입력하기 전에 현재 클래스의 잠금을 얻어야 합니다.
위 코드에서 동기화 사용은 실제로 두 번째 상황에 속합니다. 인스턴스 메서드에 직접 적용: 현재 인스턴스를 잠그는 것과 동일합니다. 동기화 코드를 입력하기 전에 현재 인스턴스의 잠금을 얻어야 합니다.
오해 가능성
우리의 멀티 스레드는 동기화에 대한 이해가 부족하여 두 스레드가 서로 다른 동기화 메서드를 호출할 때 별 문제가 되지 않는다고 생각하는 경우가 많습니다. . 인스턴스 메서드에 직접 적용: 현재 인스턴스를 잠그는 것과 동일합니다. 동기화 코드를 입력하기 전에 현재 인스턴스의 잠금을 얻어야 합니다. 동기화된 메소드를 호출하는 경우. 다른 쪽이 일반 메서드를 호출해도 상관 없으며 둘 사이에는 대기 관계가 없습니다.
이는 후속 분석에 매우 유용합니다.
Thread.sleep
은 현재 스레드(즉, 이 메서드를 호출하는 스레드)가 일정 시간 동안 실행을 일시 중지하여 다른 스레드가 계속 실행할 수 있는 기회를 제공하지만 객체 잠금을 해제하지는 않습니다. 즉, 동기화된 동기화 블록이 있는 경우 다른 스레드는 여전히 공유 데이터에 액세스할 수 없습니다. 이 방법은 예외를 포착해야 하며 이는 후속 분석에 매우 유용합니다. 자세한 내용은 System Learning Java High Concurrency Series 2를 참조하세요.
분석 프로세스:
java는 위에서 2개의 스레드가 있다고 언급했지만 여기서 스레드 우선순위를 수정하는 것은 두 프로그램이 연속적으로 실행되지 않은 경우에만 적용됩니다. , 이제 이 코드가 실행되자마자 메인 스레드인 main이 실행되었습니다. 속성 변수 int b =100의 경우 1단계(Thread t = new Thread(tt); //1)를 실행할 때 동기화 사용으로 인한 가시성 문제가 없습니다(휘발성 선언을 사용할 필요가 없음). 스레드 새로운 상태이며 아직 작업을 시작하지 않았습니다.
start 메서드가 호출될 때 2단계(t.start(); //2)를 실행하면 스레드가 실제로 시작되고 실행 가능 상태로 들어갑니다. 실행 가능 상태는 실행 가능하고 모든 것이 준비되었음을 나타냅니다. 하지만 그렇지 않습니다. 실제로 실행되는지 여부는 서비스 CPU의 스케줄링에 따라 달라집니다. 여기서 3단계를 실행하려면 먼저 잠금을 얻어야 한다(start는 네이티브 메소드를 호출해야 하고, 사용이 완료된 후에는 모든 것이 준비되지만 CPU에서 실행해야 한다는 뜻은 아니다. 실행 여부는 서비스 CPU의 스케줄링에 따라 달라집니다. run 메소드는 나중에 호출되고 m1 메소드가 실행됩니다.
사실 두 가지 동기화 방법에서 Thread.sheep을 사용하는지 여부는 중요하지 않습니다. 이는 단지 혼동의 어려움을 높이기 위한 것입니다. 3단계가 실행되면 자식 스레드는 실제로는 곧 준비가 되지만, 동기화가 존재하고 동일한 개체에 대해 작동한다는 점 때문에 자식 스레드는 기다려야 합니다. 메인 메소드의 실행 순서는 순차적이므로 3단계가 완료된 후에 4단계가 완료되어야 하며, 3단계가 완료되었으므로 하위 스레드에서는 m1을 실행할 수 있습니다.
여기서 먼저 가져오는 멀티 스레드 사이에 문제가 있습니다. 4단계에서 먼저 가져오면 메인 스레드 b=2000입니다. 하위 스레드 m1이 가져오면 b가 1000에 할당되거나 가능한 결과일 수 있습니다. 할당할 시간이 생기기 전에 4단계에서 출력됩니다. 이는 메인 스레드 b=1000 또는 메인 스레드 b=2000입니다. 여기서 6단계가 제거되면 b=가 먼저 실행되고 메인 스레드 b=가 먼저 실행됩니다. 하지만 6단계가 존재하기 때문에 어차피 메인 스레드인 b=가 앞에 있기 때문에 상황에 따라 1000이 될지 2000이 될지는 다릅니다. 그 이후에는 반드시 b=1000이 고정됩니다.
멀티스레딩에 대한 몇 가지 제안
스레드도 매우 소중하므로 스레드 풀을 사용하는 것이 좋습니다. 스레드 풀은 나중에 특히 중요하므로 주의해야 합니다. . 스레드에 이름을 지정합니다. 온라인 CPU가 높을 때 이름이 있으면 고급 jstack을 사용해야 합니다. 멀티스레딩을 사용하려면 스레드 안전 문제에 특별한 주의가 필요하며, jdk를 사용할 때 설명할 수 없는 문제가 발생하지 않도록 jdk가 스레드로부터 안전한지 또는 안전하지 않은지 이해해야 합니다.
다음 기사에서 공유할 몇 가지 기술도 있습니다. 멀티스레딩은 특히 중요하고 어렵습니다. 모두가 이에 대해 더 많은 시간을 투자하길 바랍니다.
멀티스레딩을 위한 몇 가지 디버깅 기술
중단점으로 인해 중단점을 통과할 때 모든 스레드가 중지되어야 하며, 이로 인해 이클립스에서 조건이 충족되면 조건부 중단점이 발생하게 됩니다. 시간이 되면 멈출 수 있어서 편리해요.
관련 권장 사항: Java 입문 튜토리얼
위 내용은 자바 면접 질문이 어렵다고 들었는데?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!