Multi-Thread로 동시 접근되는 것을 막는 개념.
Thread는 class의 멤버변수의 자원에 접근할 수 있는데, 이는 멤버 변수가 heap 메모리를 사용하기 때문임. 여러 thread가 공유 자원에 접근하는 경우 동기화를 해줘야 할 필요가 있어서 synchronized를 사용. 추가적으로 synchronized 외에 volatile, atomic class를 사용할 수 있음.
class BasicSynchronization {
private String msg;
public static void main(String[] args) {
BasicSynchronization tmp = new BasicSynchronization();
System.out.println("Test start");
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
tmp.callMe("Thread 1");
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
tmp.callMe("Thread 2");
}
}).start();
System.out.println("Test end!");
}
public synchronized void callMe(String whoCallMe) {
msg = whoCallMe;
try {
long sleep = (long)(Math.random() * 100);
Thread.sleep(sleep);
}
catch (InterruptedException e) {
e.printStackTrace();
}
if (!msg.equals(whoCallMe)) {
System.out.println(whoCallMe + " | " + msg);
}
}
}
테스트 결과
절대 로그가 찍히지 않음. 하지만 synchronized 함수를 제거하면 로그가 찍힘.
결과
함수에 synchronized를 걸면 그 함수가 포함된 해당 객체에 lock을 거는 것과 동일.
단점이라면 객체에 포함된 다른 모든 synchronized의 접근까지 lock이 걸림.
이를 해결하기 위해 2번 방법이 존재.
class SyncBlock1 {
public ArrayList<Integer> mList = new ArrayList<>();
public static void main(String[] args) {
SyncBlock1 syncBlock1 = new SyncBlock1();
System.out.println("Test start!");
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
syncBlock1.add(i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
syncBlock1.add(i);
}
});
t1.start();
t2.start();
// t1.join();
// t2.join();
System.out.println(syncBlock1.mList.size());
System.out.println("Test end!");
}
public void add(int val) {
synchronized(this) {
if (mList.contains(val) == false) {
mList.add(val);
}
}
}
}
Singleton은 객체를 한개만 생성하여 사용하도록 함. Multi-thread 환경과 동일하게 동작함.
static 함수라도 함수간 동기화가 잘 지켜지며, 해당 class에 lock을 검.
만약 synchronized static과 static을 함께 사용한다면 꼬일 수 있음.