다중 스레드 동기화 객체 잠금과 정적 잠금 사이의 8-잠금 문제 이해

8 잠금 문제

두 가지 문제가 있습니다: 잠금 유형과
잠그는 사람 잠금 유형: 정적 잠금은 이 클래스의 모든 인스턴스의 개체에 대한 것이고 개체 잠금은 현재
개체에만 해당됩니다 synchronized关键字. 非静态정적 잠금의 모든 방법은 synchronized키워드와 이런 종류입니다.위 두 가지 사항에 대해 판단할 때 주의: 동일한 개체인지, 개체가 호출하는 잠금 유형은 어떤 종류입니까?静态

잠금 1: 여러 스레드가 동일한 개체를 사용하여 동기화된 키워드를 사용하여 서로 다른 비정적 메서드를 호출합니다.

분석: 개체 잠금은 현재 개체에만 적용되며 잠금은 synchronized 키워드를 사용하는 모든 비정적 메서드에 적용됩니다.

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
   }
class Phone{
    
    
    synchronized void sendSMG(){
    
    
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

결과:
여기에 이미지 설명 삽입
생각 1: 为什么A线程与B线程之间启动要睡一会?
코드가 순차적으로 실행되지만 스레드가 시작되는 시기는 운영 체제에서 결정할 수 있습니다 A가 먼저 준비 상태에 들어가지만 운영 체제는 B를 먼저 시작하도록 선택할 수 있습니다. A 쓰레드와 B 쓰레드가 시작될 때 잠시 잠들게 하여 A 쓰레드가 먼저 시작되도록 합니다. 운영체제의 처리 속도가 매우 빠르기 때문에 간격이 1초라도 운영체제의 시차가 크며, A와 B가 함께 있지 않기 때문에 A가 먼저 시작할 확률이 큽니다.

생각 2: 为什么结果是这样?
두 가지 핵심 포인트:
동일한 개체이고 잠금 유형이 동일합니다
.스레드가 먼저 시작하여 개체 잠금을 얻습니다.개체 잠금이 해제됩니다), 동기화된 개체 잠금이 있는 메서드를 호출할 수 없습니다. , 기다릴 수만 있습니다. A 스레드가 실행을 마치고 잠금을 해제할 때까지 B 스레드는 잠금을 획득하고 계속 실행할 수 있습니다.

생각 3: 既然sendSMG()方法先执行,如果在方法中睡一会,sendCall()方法会不会先执行?
앞서 언급했듯이 스레드 A는 먼저 잠금을 얻은 후 sendSMG() 메서드를 실행합니다. 왜냐하면 메서드의 sleep()이 잠금을 해제하지 않기 때문입니다. 실행 시간이 너무 길더라도 이전에 잠금이 해제되지 않습니다. 실행이 완료되면 B 동기화된 객체 잠금이 있는 메서드를 실행하기 위해 잠금을 얻을 수 없습니다
생각 4: 如果Phone有一个普通方法,没有带synchronized锁,B线程启动后即使sendSMG()方法没有执行完毕,这个普通方法会被执行吗?
일반 메서드에는 객체 잠금이 없으며 B는 실행할 때 A가 잠금을 해제할 때까지 기다릴 필요가 없습니다 , B가 시작하는 한 즉시 실행될 수 있습니다. 스레드 A도 이 공통 메서드를 호출하는 경우 스레드 A와 B가 동시에 이 메서드에 있을 수 있습니다.

잠금 2: 잠금 1을 기준으로 스레드 A가 실행하는 메서드의 실행 시간을 늘려 B가 실행에 참여할 기회를 얻습니다.

위의 생각 3을 검증하기 위해 A쓰레드가 먼저 실행을 시작하는 것이 아니라 sendSMG()메소드가 먼저 실행됨을 증명하고, B쓰레드가 sendCall()메소드를 호출하지만 A쓰레드가 호출하지 않는 문제를 따라잡는다. sendSMG() 메서드를 실행하여 객체 잠금을 해제하면 B 단순히 sendCall() 메서드를 실행할 기회가 없을 것입니다. B 스레드는 일정 시간이 지나면 시작되기 때문에 A가 먼저 객체 잠금을 획득합니다. 잠금이 확보되는 한 객체 내에서 여러 스레드는 동기화된 객체 잠금으로 메서드를 실행하기 위해 하나의 스레드만 가질 수 있습니다.

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2); // 注意:我这里特意为2s使得这是B线程一定启动了 测试:B线程的sendCasll方法会不会先执行。} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

코드는 잠금 1을 기준으로 sendMsg()의 실행 시간을 늘립니다. 결과는 잠금 1과 동일합니다. 1초를 기다린 후 콘솔에 "메시지 보내기"가 표시되고 "통화"가 나타납니다.
여기에 이미지 설명 삽입
생각: 어떻게 될까요?
여기에 이미지 설명 삽입
스레드 A가 객체 잠금을 잡은 후 sendSMG() 메서드를 실행하고 콘솔에 "메시지 보내기"를 출력한 다음 1초 동안 잠자고 잠금을 해제합니다. 쓰레드 B는 잠금을 받고, sendCall() 메서드를 실행하고, 즉시 콘솔에 "call"을 출력합니다.

잠금 3: 여러 스레드가 동일한 개체를 사용하고, 하나의 스레드는 개체 잠금이 있는 메서드를 실행하고, 하나의 스레드는 일반 메서드를 실행합니다.

분석: 개체 잠금은 동기화된 비정적 메서드를 잠그고 일반 메서드는 더 이상 범위를 잠그지 않습니다
.

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendHello();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
    void sendHello(){
    
    
        System.out.println("hello");
    }
}

결과:
여기에 이미지 설명 삽입
스레드 B의 실행은 스레드 A의 객체 잠금 해제를 기다릴 필요가 없습니다. 일반 메서드는 객체 잠금을 전혀 획득할 필요가 없기 때문입니다.

생각: 스레드가 일반 메서드도 호출한 다음 이와 같이 일반 메서드를 다시 맞추면 어떻게 될까요?
여기에 이미지 설명 삽입
여기에 이미지 설명 삽입
결과:
여기에 이미지 설명 삽입


위에서는 동일한 개체를 사용하여 동기화된 개체 잠금 문제를 논의하고 다음은 不同개체를 사용하여 동기화된 개체 잠금 문제를 논의합니다.


잠금 4: 여러 스레드가 다른 개체를 사용하여 개체 잠금이 있는 다른 메서드를 호출합니다.

분석: 개체 잠금: 현재 개체에 대해서만 유효하므로 다른 개체 간에는 사용할 수 없습니다.

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        Phone phone1 = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
    
    phone1.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone2.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    
        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
    void sendHello(){
    
    
        System.out.println(Thread.currentThread().getName()+"进入");
        try {
    
     TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) {
    
     }
        System.out.println("hello");
        System.out.println(Thread.currentThread().getName()+"离开");
    }
}

결과:
여기에 이미지 설명 삽입
생각: 明明A线程先启动获取了对象锁,为什么B线程的方法在A线程没有执行完毕就开始执行?
두 가지 문제가 있습니다: 잠금 유형과 잠그는 사람
쓰레드 A와 B는 서로 다른 개체를 사용하므로 개체 잠금에 적합하지 않습니다! 쓰레드 B가 조작하는 객체는 쓰레드 B의 락을 가지고 있고, 쓰레드 A가 운용하는 객체는 쓰레드 A의 락을 가지고 있습니다. 둘은 같은 객체가 아니므로 락이 다릅니다. 객체 잠금 유형을 실행하는 메서드의 경우 스레드 A의 객체 잠금은 스레드 B의 객체 잠금을 잠글 수 없습니다.
사고: 如果锁住不同对象的?
이 때 클래스 잠금을 사용해야 합니다. used , 다른 객체에서도 인식되는 잠금을 생성합니다.


정적 잠금은 아래에 소개됩니다.


잠금 5: 여러 스레드가 동일한 개체에 대한 각각의 정적 잠금에 대한 메서드를 호출합니다.

분석: 정적 잠금, 이 유형의 모든 개체는 유효합니다. Locked는 동기화된 정적 메서드입니다.

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
     synchronized static void sendSMG(){
    
    

        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
     synchronized static void sendCall(){
    
    
        System.out.println("打电话");
    }
}

효과:
여기에 이미지 설명 삽입
메소드에 static을 추가하는 것은 동일한 객체인지 여부에 관계없이 동일한 클래스 잠금과 동일합니다. 이 유형의 인스턴스화된 객체인 한 객체가 잠금을 획득하고 해제할 때까지 어떤 객체도 실행할 수 없습니다. 클래스 잠금을 사용하는 it 메서드

잠금 6: 여러 스레드와 여러 개체가 각각의 정적 잠금에서 메서드를 호출합니다.

효과는 잠금 5와 동일합니다. 모든 클래스 잠금인 메서드에서는 동일한 개체인지 여부를 구분하지 않습니다. 개체가 실행 중인 한 동일한 개체인지 여부에 관계없이 이 클래스의 클래스 잠금이 있는 메서드가 실행될 때까지만 기다릴 수 있습니다.
생각: 如果线程A使用对象1调用的是静态锁方法,线程B使用对象2调用的是普通方法,A先启动,那么对象2调用普通方法是需要等待A释放静态锁吗?
물론 아닙니다. 일반 메서드에는 잠금이 전혀 없고 클래스 잠금이 제한할 수 없기 때문입니다.


다음은 정적 잠금과 객체 잠금을 혼합합니다.


잠금 7: 여러 스레드가 동일한 개체에 대해 개체 잠금 메서드와 정적 잠금 메서드를 각각 호출합니다.

분석: 개체 잠금과 정적 잠금이 있으며 이들 사이에는 관계가 없습니다.
또는 두 가지 핵심 사항에 주의하십시오. 동일한 대상입니까? 예, 동일한 잠금 유형이라는 메서드입니까? 不是, 하나는 정적 잠금이고 다른 하나는 객체 잠금입니다.
두 가지 다른 방법은 잠금 유형이 다르기 때문에 서로 관계가 없습니다.

public class Application {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        Phone phone = new Phone();
        new Thread(()->{
    
    phone.sendSMG();},"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
    
    phone.sendCall();},"B").start();
    }
}

class Phone{
    
    
    synchronized void sendSMG(){
    
    

        try {
    
     TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {
    
    }
        System.out.println("发送消息");
    }
    static synchronized void sendCall(){
    
    
        System.out.println("打电话");
    }
}

효과:
여기에 이미지 설명 삽입
스레드 A가 먼저 실행되지만 스레드 A는 객체 잠금을 획득합니다. 스레드 B가 시작된 후 객체 잠금으로 잠글 수 없는 클래스 잠금을 획득하므로 B는 A가 실행을 완료하고 스레드를 해제할 때까지 기다릴 필요가 없습니다. 구현하기 전에 클래스 잠금.

생각 1: 如果是两个线程调用的同一个方法加上静态锁呢?
정적 메서드에 대한 잠금은 동일한 개체인지 여부에 관계없이 하나의 스레드가 잠금을 획득하는 한 클래스 수준 잠금이기 때문에 동일한 개체인지 여부와 관련이 없습니다. 잠금은 실행 후 해제되며 다른 스레드는 이 메서드를 실행할 수 없습니다. 그러나 다른 비정적 잠금 방법을 구현할 수 있습니다.

생각 2: 如果A先拿到类锁,B后拿到对象锁,那B用等到A释放锁后再去执行吗?也就是说类锁与对象锁是否有包含关系?(다른 물체)
여기에 이미지 설명 삽입

증명: 클래스 잠금과 객체 잠금은 범위가 다른 두 가지 유형의 잠금입니다(객체 잠금은 현재 객체에만 적용되고 클래스 잠금은 이 클래스의 모든 인스턴스 객체에 적용됨). 그러나 클래스 잠금에는 객체 잠금이 포함되지 않습니다.

잠금 8: 여러 스레드의 서로 다른 개체는 개체 잠금 메서드와 정적 잠금 메서드를 각각 호출합니다.

2를 생각하는 잠금 7과 동일하게 스레드 A가 얻는 것은 이 개체 1에 속하는 개체 잠금이고 스레드 B가 얻는 것은 모든 개체에 속하는 클래스 잠금입니다. 두 호출 메서드에서 사용하는 잠금 유형이 다르므로 더 빨리 실행하는 쪽이 먼저 출력되며 기다리는 데 문제가 없습니다.

추천

출처blog.csdn.net/m0_52889702/article/details/128847416