ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Synchronized
    Java & 배경지식/기본상식 2020. 5. 2. 00:19
    반응형

    Synchronized란?

    Multi-thread로 인하여 동기화를 제어해야 하는 경우가 생긴다. 이 때 자바에서 제공하는 키워드인 Synchronized 키워드를 사용하여 Multi-thread 상태에서 동일한 자원을 동시에 접근하게 되었을 때 동시 접근을 막게 된다.

     

    즉 공유 데이터에 lock을 걸어 먼저 작업중이던 쓰레드가 작업을 완전히 끝낼 때까지 다른 쓰레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 쓰레드의 동기화를 가능하게 한다.

     

    더 쉽게 말한다면 쓰레드A가 작업하던 도중에 다른 쓰레드B에게 제어권이 넘어갔을 때, 쓰레드A가 작업하던 공유데이터를 쓰레드B가 임의로 변경하였다면, 다시 쓰레드A가 제어권을 받아서 나머지 작업을 마쳤을때 원래 의도한 것과는 다른 결과를 얻기 때문에 이를 방지하기 위해서 동기화를 해야 한다.

     

     

    synchronized를 이용한 동기화 

    자바에서는 키워드 synchronized를 통해 해당 작업과 관련된 공유데이터에 lock을 걸어서 먼저 작업 중이던 쓰레드가 작업을 완전히 마칠 때까지는 다른 쓰레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 쓰레드의 동기화를 가능하게 한다.

     

    가능한 methodsynchronized를 사용하는 메서드 단위의 동기화를 권장한다.

     

     

    특정한 객체에 lock을 걸고자 할 때

    sychronized(객체의 참조변수){
    	//...
    }

    synchronized의 시작부터 lock이 걸렸다가 블록이 끝나면 lock이 풀린다. 이 블록을 수행하는 동안은 지정된 객체에 lock이 걸려서 다른 쓰레드가 이 객체에 접근할 수 없게 된다.

     

    단점은 교착 상태(dead lock)에 빠질 수 있다.

     

    교착상태란, 두 쓰레드가 lock을 건 상태에서 서로 lock이 풀리기를 기다리는 상황으로 작업이 진행되지 않고 영원히 기다리게 만드는 상황을 말한다. 쓰레드가 교착상태에 빠지지 않도록 주의해서 프로그래밍해야 한다.

     

    메서드에 lock을 걸고자 할 때

    public synchronized void calcSum(){
    	//...
    }

    한 쓰레드가 synchronized메소드를 소출해서 수행하고 있으면, 이 메서드가 종료될 때까지 다른 쓰레드가 이 메서드를 호출 하여 수행할 수 없게 된다.

     

     

    예제

    public class SynchronizedMain {
    
    	public static void main(String[] args) {
    		SynchronizedImpl r = new SynchronizedImpl();
    		Thread t1 = new Thread(r);
    		Thread t2 = new Thread(r);
    		
    		t1.start();
    		t2.start();
    	}
    }
    public class SynchronizedImpl implements Runnable {
    	int iv = 0;
    
    	public void run() {
    		int lv = 0;
    		String name = Thread.currentThread().getName();
    
    		for (int i = 0; i < 3; i++) {
    			System.out.println(name + " Local var :  " + ++lv);
    			System.out.println(name + " InstanceVar var :  " + ++iv);
    			System.out.println();
    		}
    	} // run
    }

     

    코드 실행 결과

    위의 코드를 보게 된다면 InstanceVar는 두 개의 쓰레드가 공유하기 때문에 인스턴스 변수 iv의 값은 두 쓰레드 모두 접근이 가능하다.

    SynchronizedImpl r = new SynchronizedImpl();
    Thread t1 = new Thread(r);
    Thread t2 = new Thread(r);

    위의 상황을 그림으로 그려보면 아래와 같다.

     

     

    Thread를 상속하는 방법

    public class SynchronizedMain {
    
    	public static void main(String[] args) {
    		Data d = new Data();
    		MyThreadEx t1 = new MyThreadEx(d);
    		MyThreadEx t2 = new MyThreadEx(d);
    		
    		t1.start();
    		t2.start();
    	}
    
    }
    public class Data {
    	int iv = 0;
    }
    public class MyThreadEx extends Thread{
    	Data d;
    	
    	MyThreadEx(Data d){
    		this.d = d;
    	}
    	public void run() {
    		int lv = 0;
    		
    		for (int i = 0; i < 3; i++) {
    			System.out.println(getName() + " Local var :  " + ++lv);
    			System.out.println(getName() + " InstanceVar var :  " + ++d.iv);
    			System.out.println();
    		}
    	}
    }

     

    각각의 객체를 공유하는 경우

    public class SynchronizedMain {
    
    	public static void main(String[] args) {
    		MyThreadEx t1 = new MyThreadEx();
    		MyThreadEx t2 = new MyThreadEx();
    
    		t1.start();
    		t2.start();
    	}
    }
    public class MyThreadEx extends Thread {
    	int iv = 0;
    
    	public void run() {
    		int lv = 0;
    		for (int i = 0; i < 3; i++) {
    			System.out.println(getName() + " Local var :  " + ++lv);
    			System.out.println(getName() + " InstanceVar var :  " + ++iv);
    			System.out.println();
    		}
    	}
    }

    각각의 객체 공유 결과 그림

     

     

     

     

     

    출처

    java의 정석

    반응형

    'Java & 배경지식 > 기본상식' 카테고리의 다른 글

    클래스 로더  (0) 2020.06.07
    자바, JVM, JDK, JRE 차이  (0) 2020.06.07
    JVM의 메모리 구조  (0) 2020.04.26
    자바의 자료구조  (0) 2020.03.19
    String, StringBuffer, StringBuilder 차이  (0) 2020.03.19
Designed by Tistory.