π μ°λ λμ λκΈ°ν
λ©ν°μ°λ λ νλ‘μΈμ€μ κ²½μ° κ°μ μμμ μ¬λ¬ μ°λ λμμ 곡μ νμ¬ μ¬μ©νκΈ° λλ¬Έμ, μμμΉ λͺ»ν κ²°κ³Όλ₯Ό μ»μ μλ μλ€.
μλ₯Ό λ€μ΄ λμ μΈμΆνλ κ³Όμ μ λ κ°μ μ°λ λ μμ μΌλ‘ μκ°ν΄λ³΄μ. νμ¬ 3000μμ΄ ν΅μ₯ μκ³ μ μκ³ , μ°λ λ Aλ 3000μ μΈμΆμ μμ²νλ€. μ΄λ, μ°λ λ Bλ 1000μμ μΈμΆ μμ²νλ€. μ΄ κ²½μ°μ μ°λ λ Aκ° deposit -= money λ₯Ό μ€ννκΈ° μ μ μ°λ λ Bκ° deposit >= money ꡬ문μ μ€ννλ€λ©΄ depositμ΄ λ§μ΄λμ€κ° λ κ²μ΄λ€.
public class BankAccount {
private int deposit;
public void withdraw(int money) {
if(deposit >= money) {
deposit -= money;
System.out.println(deposit);
} else {
System.out.println("λμ μΈμΆν μ μμ΅λλ€.");
}
}
}
μ΄λ¬ν μΌμ λ§κΈ° μν΄ ν μ°λ λκ° νΉμ μμ μ λλ§μΉκΈ° μ κΉμ§ λ€λ₯Έ μ°λ λμ μμ μ€νμ κ΄λ¦¬ν νμκ° μλ€. μ΄λ₯Ό μν΄ μκ³ μμ(critical section)κ³Ό λ½(lock)μ΄λΌλ κ°λ μ΄ λμ λμλ€.
ποΈ μκ³ μμκ³Ό λ½
곡μ λ°μ΄ν°λ₯Ό μ¬μ©νλ μ½λ μμμ μκ³ μμμΌλ‘ μ§μ νκ³ , 곡μ λ°μ΄ν°κ° κ°μ§κ³ μλ λ½μ νλν μ°λ λλ§ μ΄ μκ³ μμμ μ§μ ν μ μκ² λ§λ¬μΌλ‘μ¨ μμμ λ°μν λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ€. μ΄μ²λΌ ν μ°λ λκ° μ§ν μ€μΈ μμ μ λ€λ₯Έ μ°λ λκ° μ κ·Όνμ§ λͺ»νλλ‘ λ§λ κ²μ 'μ°λ λ λκΈ°ν(synchronization)'λΌκ³ νλ€.
π μλ°μ μ°λ λ λκΈ°ν
ποΈ synchronizedλ₯Ό μ΄μ©ν λκΈ°ν
μλ°μμ λκΈ°νλ₯Ό κ°μ₯ μ½κ² λ¬μ±ν μ μλ λ°©λ²μ synchronizedλ₯Ό μ΄μ©νλ κ²μ΄λ€. synchronizedλ μκ³ μμμ λ²μμ λ°λΌ μ±λ₯μ ν¬κ² μ’μ§μ°μ§ λκΈ° λλ¬Έμ μ΅λν μ μ μκ³ μμμ μ€μ νλ κ²μ΄ μ€μνλ€.
public class BankAccount {
private int deposit;
public synchronized void withdraw(int money) { // λ©μλμ synchronizedλ₯Ό μ μΈ
if(deposit >= money) {
deposit -= money;
System.out.println(deposit);
} else {
System.out.println("λμ μΈμΆν μ μμ΅λλ€.");
}
}
}
ποΈ wait()κ³Ό notify()
μκ³ μμμ λ½μ νλνμ§ λͺ»ν μ°λ λλ μ κ·Όν μ μλ€. λ°λΌμ λ½μ νλνμ¬ μμ μ μ€ννκ³ μλ μ°λ λμ μμ μ΄ μ€λ κ±Έλ¦°λ€λ©΄ λ€λ₯Έ μ°λ λλ λͺ¨λ ν΄λΉ λ½μ νλνκΈ° μν΄ κΈ°λ€λ €μΌ νλ€. μ΄λ¬ν μν©μ κ°μ νκΈ° μν΄ wait()κ³Ό notify()λ₯Ό μ¬μ©ν μ μλ€.
λκΈ°νλ μκ³ μμμ μ½λλ₯Ό μννλ€κ° μμ μ λ μ΄μ μ§νν μ μλ€λ©΄, wait()μ νΈμΆνμ¬ μ°λ λ Aκ° λ½μ λ°λ©νκ³ κΈ°λ€λ¦¬κ² λ§λ λ€. κ·Έλ¬λ©΄ μ°λ λ Bκ° λ½μ νλνμ¬ μμ μ μνν μ μλ€. λμ€μ μμ μ μ§νν μ μλ μν©μ΄ λλ©΄ notify()λ₯Ό νΈμΆν΄μ, μ°λ λ Aκ° λ€μ μμ μ ν μ μλλ‘ νλ€.
void wait(); // notify(), notifyAll()μ΄ νΈμΆλ λ κΉμ§ κΈ°λ€λ¦°λ€.
void wait(long timeout); // μ§μ λ μκ°λμ κΈ°λ€λ¦°λ€.
void wait(long timeout, int nanos);
void notify();
void notifyAll();
class Message {
private String content;
private boolean empty = true;
public synchronized String read() {
while (empty) {
try {
wait(); // λκΈ° μνλ‘ μ ν
} catch (InterruptedException e) {
e.printStackTrace();
}
}
empty = true;
notifyAll(); // λκΈ° μ€μΈ μ€λ λλ₯Ό κΉ¨μ
return content;
}
public synchronized void write(String message) {
while (!empty) {
try {
wait(); // λκΈ° μνλ‘ μ ν
} catch (InterruptedException e) {
e.printStackTrace();
}
}
empty = false;
this.content = message;
notifyAll(); // λκΈ° μ€μΈ μ€λ λλ₯Ό κΉ¨μ
}
}
public class WaitNotifyExample {
public static void main(String[] args) {
Message message = new Message();
// μ½λ μ€λ λ
Thread readerThread = new Thread(() -> {
String receivedMessage = message.read();
System.out.println("Received: " + receivedMessage);
});
// μ°λ μ€λ λ
Thread writerThread = new Thread(() -> {
String messageToSend = "Hello World!";
message.write(messageToSend);
System.out.println("Sent: " + messageToSend);
});
// μ€λ λ μ€ν
readerThread.start();
writerThread.start();
}
}
λ§μ½ μ¬κΈ°μ μ°λ μ€λ λκ° μ¬λ¬κ°λΌλ©΄ waiting poolμλ μ½λ μ°λ λμ μ°λ μ°λ λκ° κ°μ΄ μ‘΄μ¬νκ² λλ€. μ¬κΈ°μ notify()λ₯Ό νΈμΆνκ² JVMμ λλ€μΌλ‘ νλμ μ°λ λλ₯Ό μ νν΄μ μ€ννλ€. λ§μ½ κ³μ μ½λ μ°λ λλ§ μ νμ΄ λλ€λ©΄ μ°λ μ°λ λλ μ€λ«λμ κΈ°λ€λ¦¬κ² λ μ μλ€. μ΄λ° νμμ κΈ°μ(starvation)μ΄λΌκ³ νλ€.
μ΄λ° κΈ°μ νμμ λ§κΈ° μν΄ notifyAll()μ νΈμΆν μ μμ§λ§, μ°λ μ°λ λμ μ½λ μ°λ λ λͺ¨λ ν΅μ§λ₯Ό λ°κΈ° λλ¬Έμ λ½μ μ»κΈ° μν κ²½μμ νκ² λλ€. μ΄λ₯Ό 'κ²½μ μν(race condition)'λΌκ³ νλ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄μλ μ°λ μ°λ λμ μ½λ μ°λ λλ₯Ό ꡬλ³ν΄μ ν΅μ§ν΄μΌ νλ€. μ΄λ₯Ό μν΄ Lockκ³Ό Conditionμ μ΄μ©ν μ μλ€.
ποΈ Lockκ³Ό Conditionμ μ΄μ©ν λκΈ°ν
java.util.concurrent.locks ν¨ν€μ§κ° μ 곡νλ lock ν΄λμ€λ€μ μ΄μ©νλ©΄ synchronizedλ₯Ό μ¬μ©νμ λ λ³΄λ€ λ μ μ°ν λκΈ°νλ₯Ό ν μ μλ€.
[Lock Interface]
μ°λ λκ° λͺ μμ μΌλ‘ λ½μ νλνκ³ ν΄μ ν μ μλ λ©μλλ₯Ό μ 곡νλ€.
// λ½μ νλνλ€. νλν μ μλ κ²½μ° μ°λ λλ λΈλ‘λλ€.
void lock()
// λ½μ ν΄μ νλ€. μμΈκ° λ°μν΄λ λ½μ΄ ν΄μ λ μ μλλ‘ finally λΈλ‘μμ νΈμΆλμ΄μΌ νλ€.
void unlock()
// λ½ νλμ μλνλ€. λ§μ½ λ€λ₯Έ μ€λ λμ μν΄ νμ¬ λ½μ΄ μμ λκ³ μμΌλ©΄
// trueλ₯Ό λ°ννκ³ κ·Έλ μ§ μμΌλ©΄ falseλ₯Ό λ°ννλ€.
boolean tryLock()
boolean tryLock(long time, TimeUnit unit)
// λ½κ³Ό ν¨κ» μ¬μ©ν μ μλ Condition κ°μ²΄λ₯Ό λ°ννλ€.
Condition newCondition()
[ReentrantLock]
ReentratLockμλ λ κ°μ μμ±μκ° μμΌλ©°, νλΌλ―Έν°λ‘ trueλ₯Ό λκΈ°κ² λλ©΄ μ€λ κΈ°λ€λ¦° μ°λ λκ° lockμ νλ ν μ μκ² νλ€. νμ§λ§, 곡μ νκ² μ²λ¦¬νκΈ° μν΄ μ΄λ€ μ°λ λκ° κ°μ₯ μ€λ κΈ°λ€λ Έλμ§ νμΈνλ κ³Όμ μ κ±°μΉκΈ° λλ¬Έμ μ±λ₯μ λ¨μ΄μ§λ€.
ReentrantLock()
ReentrantLock(boolean fair)
public class SharedObject {
//...
ReentrantLock lock = new ReentrantLock();
int counter = 0;
public void perform() {
lock.lock();
try {
// Critical section here
count++;
} finally {
lock.unlock();
}
}
//...
}
lock()μ lockμ μ»μ λκΉμ§ μ°λ λλ₯Ό λΈλ½μν€κΈ° λλ¬Έμ μ±λ₯μ λ¬Έμ κ° μκΈΈ μ μλ€. λ°λΌμ μλ΅μ±μ΄ μ€μν κ²½μ°, tryLock()μ μ΄μ©ν΄μ μ§μ μκ° λμ lockμ μ»μ§ λͺ»νλ©΄ λ€μ μμ μ μλν κ²μΈμ§ μ¬μ©μκ° κ²°μ νκ² νλ κ²μ΄ μ’λ€.
public void performTryLock(){
//...
boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);
if(isLockAcquired) {
try {
//Critical section here
} finally {
lock.unlock();
}
} else {
System.out.println("μνμλ©΄ λ€μ μλν΄μ£ΌμΈμ");
}
}
[ReentrantReadWriteLock]
μ½κΈ°μλ 곡μ μ μ΄κ³ , μ°κΈ°μλ λ°°νμ μΈ lockμ μ 곡νλ€. λ μμΈν λ§νλ©΄ μ½κΈ° μ°λ λκ° μκ³ μμμ μλ κ²½μ° μ½κΈ° μ μ© μ°λ λλ μ ν μλ λ½μ μ»κ² λλ€. νμ§λ§ μ°κΈ°λ₯Ό νλ κ²½μ°μλ μ°κΈ° μ°λ λ νλλ§ λ½μ κ°μ§ μ μλ€.
public class ReadWriteMap<K, V> {
private final Map<K, V> map = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void put(K key, V value) {
lock.writeLock().lock();
try {
// μ°κΈ° μμ
μν
map.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
public V get(K key) {
lock.readLock().lock();
try {
// μ½κΈ° μμ
μν
return map.get(key);
} finally {
lock.readLock().unlock();
}
}
}
[StampedLock]
lockμ κ±Έκ±°λ ν΄μ§ν λ μ€ν¬ν(long νμ μ μ μκ°)λ₯Ό μ¬μ©νλ©°, μ½κΈ°μ μ°κΈ°λ₯Ό μν μ κΈμΈμ λκ΄μ μ½κΈ° μ κΈ(optimisitc reading lock)μ μ 곡νλ€. λκ΄μ μ½κΈ° μ κΈμμλ μ½κΈ° μ κΈμ΄ κ±Έλ €μμ λ, μ°κΈ° μ κΈ μμ²μ΄ λ€μ΄μ€λ©΄ μ½κΈ° μ κΈμ κΈ°λ€λ¦¬λ κ²μ΄ μλλΌ μ½κΈ° μ κΈμ λ°μ΄λΈλ€.
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private double x;
private double y;
public void move(double deltaX, double deltaY) {
long stamp = lock.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp);
}
}
public double distanceFromOrigin() {
long stamp = lock.tryOptimisticRead();
double currentX = x;
double currentY = y;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
move() λ©μλλ μ°κΈ° μ κΈμ νλνμ¬ μ’νλ₯Ό μμ νκ³ , distanceFromOrigin() λ©μλλ μ΅μ νλ μ½κΈ° μ κΈμ μλνμ¬ μ’νλ₯Ό μ½λλ€. validate()λ μ€ν¬νλ₯Ό μΈμλ‘ λ°μΌλ©°, ν΄λΉ μ€ν¬νκ° μ μ©νμ§ νμΈνλ€. μ ν¨μ± κ²μ¬λ λ°μ΄ν°μ λν μ°κΈ° μμ μ΄ λ°μνλμ§ μ¬λΆλ₯Ό νμΈνμ¬ μνλλ€. λ§μ½ falseλ₯Ό λ°ννλ©΄ λ°μ΄ν°μ λν μ°κΈ° μμ μ΄ λ°μν κ²μΌλ‘ λ€μ μ½κΈ° μμ μ μννλ€.
[Condition]
Conditionμ μ΄λ―Έ μμ±λ lockμΌλ‘λΆν° newCondition()μ νΈμΆν΄μ μμ±ν μ μμΌλ©° λΆλ¦¬λ waiting poolμ μ¬μ©ν μ μλλ‘ λμμ€λ€.
private ReentrantLock lock = new ReentrantLock();
private Condition readLock = lock.newCondition();
private Condition writeLock = lock.newCondition();
wait() & notify() λμ Conditionμμλ await() & signal()μ μ¬μ©νλ€.
public class ConditionExample {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean conditionMet = false;
public void doSomething() {
lock.lock();
try {
while (!conditionMet) {
condition.await();
}
// μ‘°κ±΄μ΄ μΆ©μ‘±λμ λ μνν μμ
} catch (InterruptedException e) {
// μΈν°λ½νΈ μμΈ μ²λ¦¬
} finally {
lock.unlock();
}
}
public void notifyCondition() {
lock.lock();
try {
conditionMet = true;
condition.signal();
} finally {
lock.unlock();
}
}
}