java 기본 강좌의synchronized 키워드

이 장에서synchronized 키워드에 대해 소개합니다.관련된 내용은 다음과 같다. 1.synchronized 원리 2.synchronized 기본 규칙 3.synchronized 방법과 synchronized 코드 블록 4.인스턴스 잠금 및 글로벌 잠금
1. synchronized 원리
자바에서는 모든 대상이 있고 동기화 자물쇠만 있습니다.이것 또한 동기화 자물쇠는 대상에 의존하여 존재한다는 것을 의미한다.우리가 어떤 대상의synchronized 방법을 호출할 때, 이 대상의 동기화 자물쇠를 가져옵니다.예를 들어synchronized(obj)는'obj 이 대상'의 동기화 자물쇠를 가져옵니다.서로 다른 라인이 동기화 자물쇠에 대한 접근은 서로 배제된다.즉, 어느 시간에 대상의 동기화 자물쇠는 한 라인에서만 얻을 수 있다!동기화 자물쇠를 통해 우리는 다중 노드에서'대상/방법'에 대한 상호 배척 접근을 실현할 수 있다.예를 들어, 현재 두 개의 스레드 A와 스레드 B가 있는데, 모두'대상 obj의 동기화 자물쇠'에 접근할 것이다.가령, 어느 순간에 스레드 A가'obj의 동기화 자물쇠'를 얻고 일부 조작을 실행한다고 가정한다.이때 스레드 B도'obj의 동기화 자물쇠'를 가져오려고 합니다. 스레드 B는 가져오는 데 실패합니다. 스레드 A가'이 대상의 동기화 자물쇠'를 푼 후에야 스레드 B가'obj의 동기화 자물쇠'를 가져와 실행할 수 있도록 기다려야 합니다.2. synchronized 기본 규칙
우리는synchronized의 기본 규칙을 아래 세 가지로 요약하고 실례를 통해 그것들에 대해 설명할 것이다.첫 번째: 한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때, 다른 라인이'이 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 대한 접근이 막힙니다.제2조: 한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때 다른 라인은'이 대상'의 비동기화 코드 블록에 접근할 수 있다.제3조: 한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때 다른 라인이'이 대상'의 다른'synchronized 방법'이나'synchronized 코드 블록'에 대한 접근이 막힌다.
제1조
한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때, 다른 라인이'이 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 대한 접근이 막힙니다.다음은'synchronized 코드 블록'에 대응하는 프레젠테이션 프로그램입니다.

class MyRunable implements Runnable {

    @Override
    public void run() {
        synchronized(this) {
            try { 
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 100ms
                    System.out.println(Thread.currentThread().getName() + " loop " + i); 
                }
            } catch (InterruptedException ie) { 
            }
        } 
    }
}

public class Demo1_1 {

    public static void main(String[] args) { 
        Runnable demo = new MyRunable();     // “Runnable ”

        Thread t1 = new Thread(demo, "t1");  // “ t1”, t1 demo Runnable
        Thread t2 = new Thread(demo, "t2");  // “ t2”, t2 demo Runnable
        t1.start();                          // “ t1”
        t2.start();                          // “ t2”
    }
}

실행 결과:

t1 loop 0
t1 loop 1
t1 loop 2
t1 loop 3
t1 loop 4
t2 loop 0
t2 loop 1
t2 loop 2
t2 loop 3
t2 loop 4
결과 설명:run() 방법에'synchronized(this) 코드 블록'이 존재하고 t1과 t2는 모두'demo라는 Runnable 대상'을 기반으로 만든 라인입니다.이것은 우리가synchronized(this)의this를'demo라는 Runnable 대상'으로 볼 수 있음을 의미한다.따라서 루트 t1과 t2는 "demo 대상의 동기화 자물쇠"를 공유합니다.따라서 한 라인이 실행될 때, 다른 라인은'실행 라인'이'demo의 동기화 자물쇠'를 풀어야 실행할 수 있습니다.
만약 네가 확인한다면, 너는 이 문제를 분명히 할 것이다.그러면 우리는 위의 코드를 수정한 후에 다시 실행해서 결과가 어떤지, 네가 헷갈릴지 봅시다.수정된 소스 코드는 다음과 같습니다.

class MyThread extends Thread {

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        synchronized(this) {
            try { 
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 100ms
                    System.out.println(Thread.currentThread().getName() + " loop " + i); 
                }
            } catch (InterruptedException ie) { 
            }
        } 
    }
}

public class Demo1_2 {

    public static void main(String[] args) { 
        Thread t1 = new MyThread("t1");  // “ t1”
        Thread t2 = new MyThread("t2");  // “ t2”
        t1.start();                          // “ t1”
        t2.start();                          // “ t2”
    }
}

코드 설명: 비교 Demo1_2 및 Demo1_1, 발견, Demo1_2의 MyThread 클래스는 Thread에 직접 계승되고 t1과 t2는 모두 MyThread 서브라인입니다.다행히도 "Demo1_2의run() 방법"에서synchronized(this)를 호출했습니다. "Demo1_1의run() 방법"에서synchronized(this)를 호출한 것처럼!그럼, Demo1_2의 실행 프로세스는 Demo1_1 똑같은데?실행 결과:

t1 loop 0
t2 loop 0
t1 loop 1
t2 loop 1
t1 loop 2
t2 loop 2
t1 loop 3
t2 loop 3
t1 loop 4
t2 loop 4
결과 설명: 만약 이 결과가 조금도 당신을 놀라게 하지 않는다면, 나는synchronized와this에 대한 인식이 비교적 깊었다고 믿는다.그렇지 않으면 이곳의 분석을 계속 읽어 주십시오.synchronized(this)의this는'현재의 클래스 대상', 즉synchronized(this)가 있는 클래스에 대응하는 현재 대상을 가리킨다.그것의 역할은 현재 대상의 동기화 자물쇠를 가져오는 것이다.Demo1_2에서synchronized(this)의this는 MyThread 대상을 대표하고 t1과 t2는 서로 다른 MyThread 대상이기 때문에 t1과 t2는synchronized(this)를 실행할 때 서로 다른 대상의 동기화 자물쇠를 얻는다.Demo1_1 synchronized(this)의this는 MyRunable 대상을 대표한다.t1과 t2는 모두 MyRunable 대상입니다. 따라서 한 라인이 대상의 동기화 자물쇠를 가져와 다른 라인을 기다립니다.
제2조
한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때, 다른 라인은 여전히'이 대상'의 비동기화 코드 블록에 접근할 수 있다.다음은'synchronized 코드 블록'에 대응하는 프레젠테이션 프로그램입니다.

class Count {

    // synchronized
    public void synMethod() {
        synchronized(this) {
            try { 
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 100ms
                    System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); 
                }
            } catch (InterruptedException ie) { 
            }
        } 
    }

    //
    public void nonSynMethod() {
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); 
            }
        } catch (InterruptedException ie) { 
        }
    }
}

public class Demo2 {

    public static void main(String[] args) { 
        final Count count = new Count();
        // t1, t1 “count ” synMethod()
        Thread t1 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.synMethod();
                    }
                }, "t1");

        // t2, t2 “count ” nonSynMethod()
        Thread t2 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.nonSynMethod();
                    }
                }, "t2"); 


        t1.start();  // t1
        t2.start();  // t2
    }
}

실행 결과:

t1 synMethod loop 0
t2 nonSynMethod loop 0
t1 synMethod loop 1
t2 nonSynMethod loop 1
t1 synMethod loop 2
t2 nonSynMethod loop 2
t1 synMethod loop 3
t2 nonSynMethod loop 3
t1 synMethod loop 4
t2 nonSynMethod loop 4
결과 설명: 메인 라인에 두 개의 하위 라인 t1과 t2가 새로 만들어졌습니다.t1은count 대상의synMethod() 방법을 호출합니다. 이 방법에는 동기화 블록이 포함되어 있습니다.t2는 count 대상의 nonsynMethod () 방법을 호출합니다. 이 방법은 동기화 방법이 아닙니다.t1이 실행될 때synchronized (this) 를 호출하여 "count의 동기화 자물쇠"를 가져옵니다.그러나 t2가 막히지 않았습니다. 왜냐하면 t2가'count'동기화 자물쇠를 사용하지 않았기 때문입니다.
제3조
한 라인이'어떤 대상'의'synchronized 방법'이나'synchronized 코드 블록'에 접근할 때, 다른 라인이'이 대상'의 다른'synchronized 방법'이나'synchronized 코드 블록'에 대한 접근이 막힙니다.우리는 위의 예 중의 nonsynMethod () 방법체도 synchronized (this) 로 수식할 것이다.수정된 소스 코드는 다음과 같습니다.

class Count {

    // synchronized
    public void synMethod() {
        synchronized(this) {
            try { 
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100); // 100ms
                    System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); 
                }
            } catch (InterruptedException ie) { 
            }
        } 
    }

    // synchronized
    public void nonSynMethod() {
        synchronized(this) {
            try { 
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); 
                }
            } catch (InterruptedException ie) { 
            }
        }
    }
}

public class Demo3 {

    public static void main(String[] args) { 
        final Count count = new Count();
        // t1, t1 “count ” synMethod()
        Thread t1 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.synMethod();
                    }
                }, "t1");

        // t2, t2 “count ” nonSynMethod()
        Thread t2 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        count.nonSynMethod();
                    }
                }, "t2"); 


        t1.start();  // t1
        t2.start();  // t2
    }
}

(일부) 실행 결과:

synMethod() : 11
synBlock() : 3
 
4. 인스턴스 잠금 및 글로벌 잠금 인스턴스 잠금 - 인스턴스 객체에 잠급니다.만약 이 종류가 단례라면, 이 자물쇠도 전역 자물쇠의 개념을 가지고 있다.실례 자물쇠에 대응하는 것은synchronized 키워드입니다.전역 자물쇠 - 이 자물쇠는 클래스를 대상으로 하고 실례가 몇 개 있든지 간에 이 자물쇠를 공유합니다.전역 자물쇠에 대응하는 것은 staticsynchronized (또는 이 종류의class나classloader 대상에 잠긴 것) 이다.
인스턴스 잠금과 글로벌 잠금에 대한 이미지는 다음과 같습니다.

pulbic class Something {
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}
가령, Something에는 두 개의 인스턴스 x와 y가 있습니다.다음 네 그룹의 표현식으로 얻은 자물쇠의 상황을 분석합니다.(01) x.isSynca() 및 x.isSyncB()(02) x.isSynca() 및 y.isSynca()(03) x.cSynca() 및 y.cSyncB()(04) x.isSynca() 및 Something.cSyncA()
(01) 동시에 액세스할 수 없습니다.isSynca () 와 isSyncB () 는 같은 객체 (객체 x) 에 액세스하는 동기화 잠금이기 때문입니다!

// LockTest2.java
class Something {
    public synchronized void isSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public synchronized void isSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
}

public class LockTest2 {

    Something x = new Something();
    Something y = new Something();

    // (02) x.isSyncA() y.isSyncA()
    private void test2() {
        // t21, t21 x.isSyncA()
        Thread t21 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        x.isSyncA();
                    }
                }, "t21");

        // t22, t22 x.isSyncB()
        Thread t22 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        y.isSyncA();
                    }
                }, "t22"); 


        t21.start();  // t21
        t22.start();  // t22
    }

    public static void main(String[] args) {
        LockTest2 demo = new LockTest2();

        demo.test2();
    }
}

실행 결과:

t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t11 : isSyncA
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB
t12 : isSyncB
(02) 동시에 액세스할 수 있습니다.같은 대상의 동기화 자물쇠에 접근하지 않기 때문에, x.isSynca()는 x의 동기화 자물쇠에 접근하고, y.isSynca()는 y의 동기화 자물쇠에 접근한다.

// LockTest2.java
class Something {
    public synchronized void isSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public synchronized void isSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
}

public class LockTest2 {

    Something x = new Something();
    Something y = new Something();

    // (02) x.isSyncA() y.isSyncA()
    private void test2() {
        // t21, t21 x.isSyncA()
        Thread t21 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        x.isSyncA();
                    }
                }, "t21");

        // t22, t22 x.isSyncB()
        Thread t22 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        y.isSyncA();
                    }
                }, "t22"); 


        t21.start();  // t21
        t22.start();  // t22
    }

    public static void main(String[] args) {
        LockTest2 demo = new LockTest2();

        demo.test2();
    }
}

실행 결과:

t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
t21 : isSyncA
t22 : isSyncA
(03)에 동시 액세스할 수 없습니다.cSynca()와 cSyncB()는 모두 static 유형이기 때문에 x.cSynca()는 Something에 해당합니다.isSynca(), y.cSyncB()는 Something에 해당합니다.isSyncB(), 따라서 동기식 자물쇠를 함께 사용하기 때문에 동시에 반문할 수 없습니다.

// LockTest3.java
class Something {
    public synchronized void isSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public synchronized void isSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
}

public class LockTest3 {

    Something x = new Something();
    Something y = new Something();

    // (03) x.cSyncA() y.cSyncB()
    private void test3() {
        // t31, t31 x.isSyncA()
        Thread t31 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        x.cSyncA();
                    }
                }, "t31");

        // t32, t32 x.isSyncB()
        Thread t32 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        y.cSyncB();
                    }
                }, "t32"); 


        t31.start();  // t31
        t32.start();  // t32
    }

    public static void main(String[] args) {
        LockTest3 demo = new LockTest3();

        demo.test3();
    }
}

실행 결과:

t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t31 : cSyncA
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB
t32 : cSyncB
(04) 동시에 액세스할 수 있습니다.isSynca()는 실례 방법이기 때문에 x.isSynca()는 대상 x의 자물쇠를 사용합니다.cSynca()는 정적 방법입니다. Something.cSynca()는 클래스 잠금을 사용하는 것을 이해할 수 있습니다.따라서 동시에 액세스할 수 있습니다.

// LockTest4.java
class Something {
    public synchronized void isSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public synchronized void isSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : isSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncA(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncA");
            }
        }catch (InterruptedException ie) { 
        } 
    }
    public static synchronized void cSyncB(){
        try { 
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100); // 100ms
                System.out.println(Thread.currentThread().getName()+" : cSyncB");
            }
        }catch (InterruptedException ie) { 
        } 
    }
}

public class LockTest4 {

    Something x = new Something();
    Something y = new Something();

    // (04) x.isSyncA() Something.cSyncA()
    private void test4() {
        // t41, t41 x.isSyncA()
        Thread t41 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        x.isSyncA();
                    }
                }, "t41");

        // t42, t42 x.isSyncB()
        Thread t42 = new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        Something.cSyncA();
                    }
                }, "t42"); 


        t41.start();  // t41
        t42.start();  // t42
    }

    public static void main(String[] args) {
        LockTest4 demo = new LockTest4();

        demo.test4();
    }
}

운영 결과:

t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA
t41 : isSyncA
t42 : cSyncA

좋은 웹페이지 즐겨찾기