Lang/Java

간단하게 Java synchronized 사용 방법

hamaganatanadda 2023. 7. 1. 16:13

JDK 1.8

 

전체 코드

Thread는 2개로 이름은 A, B로 지정

A는 B보다 1초 늦게 시작하게 처리

각 메소드는 5초 대기한다.

 

간단히

synchronized는 static이나 공유하는 Class에서 대해서는 동시 접근이 되지 않는다. 해당 메소드가 아닌 class내의 모든 synchronized에 접근이 되지 않음

synchronized를 사용하지 않는 메소드는 접근이 가능하다.

 

class SyncClass{
    public void basic(String name) {
        System.out.println("basic: " + name);
    }
    
    private Object lockObject = new Object();    

    public void objectSyncTest(String name, int time) {
        synchronized (lockObject) {
            try {
                System.out.println("objectSyncTest start: " + name);
                Thread.sleep(1000*time);
                System.out.println("objectSyncTest end: " + name);
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void thisSyncTest(String name, int time) {
        synchronized (this) {
            try {
                System.out.println("thisSyncTest start: " + name);
                Thread.sleep(1000*time);
                System.out.println("thisSyncTest end: " + name);
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void classSyncTest(String name, int time) {
        synchronized (SyncClass.class) {
            try {
                System.out.println("classSyncTest start: " + name);
                Thread.sleep(1000*time);
                System.out.println("classSyncTest end: " + name);
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void thisSyncTest2(String name, int time) {
        synchronized (this) {
            try {
                System.out.println("thisSyncTest2 start: " + name);
                Thread.sleep(1000*time);
                System.out.println("thisSyncTest2 end: " + name);
            } catch (InterruptedException e) {
            }
        }
    }
    
    public void thisSyncPartTest(String name, int time) {
        synchronized (this) {
            try {
                System.out.println("thisSyncPartTest this start: " + name);
                Thread.sleep(1000*2);
                System.out.println("thisSyncPartTest this end: " + name);
            } catch (InterruptedException e) {
            }
        }
        try {
            System.out.println("thisSyncPartTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("thisSyncPartTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public synchronized void syncTest(String name, int time) {
        try {
            System.out.println("syncTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("syncTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public synchronized void syncTest2(String name, int time) {
        try {
            System.out.println("syncTest2 start: " + name);
            Thread.sleep(1000*time);
            System.out.println("syncTest2 end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public static synchronized void syncStaticTest(String name, int time) {
        try {
            System.out.println("syncStaticTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("syncStaticTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public static synchronized void syncStaticTest2(String name, int time) {
        try {
            System.out.println("syncStaticTest2 start: " + name);
            Thread.sleep(1000*time);
            System.out.println("syncStaticTest2 end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public static void staticTest(String name, int time) {
        try {
            System.out.println("staticTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("staticTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
    
    public static void thisSyncStaticTest(String name, int time) {
        synchronized (SyncClass.class) {
            try {
                System.out.println("thisSyncStaticTest start: " + name);
                Thread.sleep(1000*time);
                System.out.println("thisSyncStaticTest end: " + name);
            } catch (InterruptedException e) {
            }
        }
    }
}


public class ThreadTest {
    static SyncClass staticSyncClass = new SyncClass();
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new Worker());
        Thread thread2 = new Thread(new Worker());
        thread1.setName("A");
        thread2.setName("B");
        
        thread1.start();
        thread2.start();
    }
    
    private static class Worker implements Runnable {    	
        @Override
        public void run() {  
            String name = Thread.currentThread().getName();
            
            if(name.equals("A")) {
                try {
                    Thread.sleep(1000*1);                    
                } catch (InterruptedException e) {
                }
//                SyncClass syncClass = new SyncClass();
//                syncClass.thisSyncTest(name, 5);                
            }else if(name.equals("B")) {
//                SyncClass syncClass = new SyncClass();
//                syncClass.thisSyncTest(name, 5);
            }
        }
    }
}

 

동시에 접근 가능

public void basic(String name) {
    System.out.println("basic: " + name);
}

 

호출1

Thread별로 클래스를 생성 하였기 때문에 동시에 접근 가능하다.

호출2

1개의 Class를 공유해서 사용 -> 이전 결과와 다르게 B가 끝나야 A가 실행이 된다.

 

Class를 공유해서 사용하고 synchronized를 사용하면 다른 모든 synchronized도 lock이 걸림

public synchronized void syncTest(String name, int time) {
    try {
        System.out.println("syncTest: " + name);
        Thread.sleep(1000*time);
    } catch (InterruptedException e) {
    }
}

//위와 동일하다
public void thisSyncTest(String name, int time) {
    synchronized (this) {
        try {
            System.out.println("thisSyncTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("thisSyncTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
}

호출1
SyncClass syncClass = new SyncClass();
syncClass.syncTest(name, 10);

결과1
syncTest start: B
syncTest start: A
syncTest end: B
syncTest end: A

호출2
staticSyncClass.syncTest(name, 5);

결과2
syncTest start: B
syncTest end: B
syncTest start: A
syncTest end: A

 

static

호출1, 호출2를 보면 synchronized 메소드들은 동시에 접근이 되지 않는다.

호출3, 호출4를 보면 synchronized사용하지 않은 메소드는 바로 접근이 된다.

public static synchronized void syncStaticTest(String name, int time) {
    try {
        System.out.println("syncStaticTest start: " + name);
        Thread.sleep(1000*time);
        System.out.println("syncStaticTest end: " + name);
    } catch (InterruptedException e) {
    }
}
//위와 동일하다
public static void thisSyncStaticTest(String name, int time) {
    synchronized (SyncClass.class) {
        try {
            System.out.println("thisSyncStaticTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("thisSyncStaticTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
}

public static synchronized void syncStaticTest2(String name, int time) {
    try {
        System.out.println("syncStaticTest2 start: " + name);
        Thread.sleep(1000*time);
        System.out.println("syncStaticTest2 end: " + name);
    } catch (InterruptedException e) {
    }
}

public static void staticTest(String name, int time) {
    try {
        System.out.println("staticTest start: " + name);
        Thread.sleep(1000*time);
        System.out.println("staticTest end: " + name);
    } catch (InterruptedException e) {
    }
}

public void basic(String name) {
    System.out.println("basic: " + name);
}
    
호출1
SyncClass.syncStaticTest(name, 5);
결과1
syncStaticTest start: B
syncStaticTest end: B
syncStaticTest start: A
syncStaticTest end: A

호출2
Thread1: SyncClass.syncStaticTest2(name, 5);
Thread2: SyncClass.syncStaticTest(name, 5);
결과2
syncStaticTest start: B
syncStaticTest end: B
syncStaticTest start: A
syncStaticTest end: A

호출3
Thread1: 
SyncClass syncClass = new SyncClass();
syncClass.basic(name);
Thread2: SyncClass.syncStaticTest(name, 5);
결과3
syncStaticTest start: B
basic: A
syncStaticTest end: B

호출4
Thread1: SyncClass.staticTest(name, 5);
Thread2: SyncClass.syncStaticTest(name, 5);
결과4
syncStaticTest start: B
staticTest start: A
syncStaticTest end: B
staticTest end: A

 

부분적으로.

synchronized 부분만 lock이 걸린다. 동일하게 class를 공유하여 사용 시에만 해당된다.

public void thisSyncPartTest(String name, int time) {
    synchronized (this) {
        try {
            System.out.println("thisSyncPartTest this start: " + name);
            Thread.sleep(1000*2);
            System.out.println("thisSyncPartTest this end: " + name);
        } catch (InterruptedException e) {
        }
    }
    try {
        System.out.println("thisSyncPartTest start: " + name);
        Thread.sleep(1000*time);
        System.out.println("thisSyncPartTest end: " + name);
    } catch (InterruptedException e) {
    }
}

호출1 
staticSyncClass.thisSyncPartTest(name, 5);
결과1
thisSyncPartTest this start: B
thisSyncPartTest this end: B
thisSyncPartTest start: B <- synchronized 끝난 시점
thisSyncPartTest this start: A
thisSyncPartTest this end: A
thisSyncPartTest start: A
thisSyncPartTest end: B
thisSyncPartTest end: A

호출2
SyncClass syncClass = new SyncClass();
syncClass.thisSyncPartTest(name, 5);
결과2
thisSyncPartTest this start: B
thisSyncPartTest this start: A
thisSyncPartTest this end: B
thisSyncPartTest start: B
thisSyncPartTest this end: A
thisSyncPartTest start: A
thisSyncPartTest end: B
thisSyncPartTest end: A

 

this와 class 차이

Class를 사용하면 새로 생성해도 동시에 접근 못 한다.

https://stackoverflow.com/questions/9056190/what-is-the-difference-between-synchronizedthis-and-synchronizedclassname-cla

 

What is the difference between synchronized(this) and synchronized(ClassName.class)?

I read somewhere that synchronized(this) should be avoided for various reasons. Yet some respectable code that I encountered uses the following in the constructor: public SomeClass(Context context...

stackoverflow.com

public void thisSyncTest(String name, int time) {
    synchronized (this) {
        try {
            System.out.println("thisSyncTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("thisSyncTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
}

public void classSyncTest(String name, int time) {
    synchronized (SyncClass.class) {
        try {
            System.out.println("classSyncTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("classSyncTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
}

호출1
SyncClass syncClass = new SyncClass();
syncClass.thisSyncTest(name, 5);
결과1
thisSyncTest start: B
thisSyncTest start: A
thisSyncTest end: B
thisSyncTest end: A

호출2
SyncClass syncClass = new SyncClass();
syncClass.classSyncTest(name, 5);
결과2
classSyncTest start: B
classSyncTest end: B
classSyncTest start: A
classSyncTest end: A

Object를 사용하는 방법도 있다. this하고 비슷하게 동작하는거 같고 검색 해보면 공격을 방지한다고 한다.

현재 내 코드에서는 비슷한 샘플을 만들 수 없을거 같다.

baeldung 여기에 좋은 샘플이 있다.

https://stackoverflow.com/questions/12397427/what-is-different-between-method-synchronized-vs-object-synchronized

 

What is different between method synchronized vs object synchronized ?

Possible Duplicate: synchronized block vs synchronized method? If anyone can help me with real example about what is different between method synchronized vs object synchronized?, it would be ...

stackoverflow.com

https://www.baeldung.com/java-synchronization-bad-practices

private Object lockObject = new Object();    

public void objectSyncTest(String name, int time) {
    synchronized (lockObject) {
        try {
            System.out.println("objectSyncTest start: " + name);
            Thread.sleep(1000*time);
            System.out.println("objectSyncTest end: " + name);
        } catch (InterruptedException e) {
        }
    }
}