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) {
}
}
}