본문 바로가기
Study/Back

Sep.19.Tue.2023 Java 수업 17일차 Synchronized / Graph

by Jobsoony 2023. 9. 19.
728x90
반응형
  •  Synchronized란?
      다중 스레드 환경에서 동시 접근으로부터 데이터의 일관성을 보호하기 위해 사용되는 키워드이다.
      하나의 스레드가 특정 블록 또는 메서드를 실행하는 동안 다른 스레드가 동일한 블록 또는 메서드에 동시에 접근하지 못하도록 하는데,
      이는 스레드 간의 경쟁 조건과 데이터 불일치 문제를 방지하는 데 도움이 된다.
     
      - 메서드 동기화 : 메서드 전체를 동기화하는 경우, 해당 메서드의 실행 중에는 다른 스레드가 해당 메서드에 접근할 수 없다.
      ex) public synchronized void syncMethod() {
                   // 동기화된 메서드 내용
           }
      - 블록 동기화 : 특정 블록을 동기화하는 경우 블록 내부의 코드만 가능
      ex) public void someMethod () {
                   // 비동기코드
                   synchronized () {
                                // 동기화된 블록 내용
                   }
                  // 비동기코드
           }
  • synchronized count : 변수에 대한 동시 접근을 동기화, 스레드 간 경쟁 조건이 발생하지 않아 count 변수가 안전하게 증가하고 데이터의 일관성을 보존한다.
public class SyncMethod {
	private int count = 0;
	private Object lockObj = new Object(); // 동기화를 위한 객체
	
	// 메서드 레벨 동기화를 사용해서 count 변수를 증가시킴
	// 한 번에 하나의 스레드만 접근할 수 있도록 보장한다.
	public synchronized void increment() {
		count++;
	}
	
	/*
	  pTask : 동기화된 블로고가 함께 사용한다.
	  synchronized(lockObj) : "코드 블록 내에서 count 변수를 증가시키기 전에 lockObj를 사용하여 동기를 수행
	  lockObj 사용하는 이유 : 다른 메서드에서 동기화에 사용할 수 있고, 다른 객체에 대한 동기화와 분리되어 충돌을 방지할 수 있다.
	  
	 */
	
	public void pTask() {
		// 다른 비동기 코드
		synchronized(lockObj) {
			// lockObj를 사용한 동기화된 블록
			 count++;
		}
		// 다른 비동기 코드
	}
	
	// SyncMethod 클래스의 인스턴스를 생성하고 Runnable 구현하여 스레드 생서 
	public static void main(String[] args) {
		SyncMethod sync = new SyncMethod();
		// Runnable 인터페이스 구현을 사용해서 스레드 생성
		//  스레드 생성
		
		// thread1
		Runnable task1 = new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					sync.increment();
				}
			}
		};
		
		// thread2
		Runnable task2 = new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 1000; i++) {
					sync.increment();
				}
			}
		};
		
		// 스레드 생성 및 시작
		Thread thread1 = new Thread(task1);
		Thread thread2 = new Thread(task2);
		
		thread1.start();
		thread2.start();
		
		// 모든 스레드가 종료될 떄 까지 대기
		try {
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("최종카운트 : " + sync.count);
	}
}

 

  • Graph란?
    여러 노드(또는 정점)와 그 노드들을 연결하는 엣지(간선)으로 구성된다.
    네트워크, 지도, 소셜 네트워크, 경로 찾기 등에 사용된다.

    인접리스트(Adjacency List) :  각 노드마다 해당 노드와 연결된 다른 노드들을 리스트로 저장하는 방식
/*
 Graph Class : 인접리스트를 사용하여 노드와 간선을 저장한다.
 */
public class Graph {
	private int V; // 그래프의 노드 수
	private LinkedList<Integer>[] adjList; // 인접리스트 배열
	// adjList : 인접 리스트를 저장하는 배열로 각 노드마다 연결된 노드들을 저장하기 위해 LinkedList 객체 배열로 초기화 한다.
			
	// 그래프 생성자 : 그래프 크기(노드 수)를 받아와서 그래프를 초기화하고 adjList 배열의 각 원소를 빈 LinkedList로 초기화

	public Graph (int ver) {
		V = ver;
		adjList = new LinkedList[ver];
		for(int i = 0; i < ver; i++) {
			adjList[i] = new LinkedList<>();
		}
	}
	
	// addEdge : 그래프에 새로운 간선(노드)를 추가
	public void addEdge (int source, int des) {
		// source에서 des로 가는 Edge를 추가하면 adjList 배열에서 source에 해당하는 LinkedList에 des(목적지)를 추가한다.
		adjList[source].add(des);
	}
	
	// 그래프 탐색(깊이 우선 탐색) : 깊이 우선 탐색을 수행하고, 시작 노드를 매개변수로 받는다.
	public void DFS(int startNode) {
		// 방문 여부를 추적하기 위한 visited 배열초기화
		boolean[] visited = new boolean[V];
		DFSUtil(startNode, visited);
	}
	
	// 실제 깊이 우선 탐색 수행
	private void DFSUtil(int currentNode, boolean[] visited) {
		// 현재 노드를 방문한 것으로 표시하고 노드 번호를 출력한다
		visited[currentNode] = true;
		System.out.print(currentNode + " "); // 노드번호 출력
		// 현재 노드와 연결된 인접 노드를 확인하고, 방문하지 않은 인접 노드를 재귀적(원래의 자리로 돌아오거나 되돌아오는)으로 방문한다.	
		for (int neighbor : adjList[currentNode]) {
			if ( !visited[neighbor]) {
				DFSUtil(neighbor, visited);
			}
		}
		
	}
}
public class GraphMain {

	public static void main(String[] args) {
		int ver = 5; // 그래프 노드의 수를 5로 정의
		Graph graph = new Graph(ver);
		
		// addEdge 간선 추가하겠다.
		graph.addEdge(0, 1); // 노드 0에서 노드 1로 방향이 있는 엣지를 추가
		graph.addEdge(0, 2); // 노드 0에서 노드 2로 방향이 있는 엣지를 추가
		graph.addEdge(1, 3); // 노드 1에서 노드 3으로 방향이 있는 엣지를 추가
		graph.addEdge(2, 4); // 노드 2에서 노드 4로 방향이 있는 모자를 추가
		
		System.out.println("DFS 탐색 결과 : ");
		// DFS 탐색을 시작할 노드의 번호를 전달하고 0으로 시작.
		graph.DFS(0);
	}
}

// DFS 탐색을 시작하여 그 결과를 출력하고 탐색 결과는 시작 노드에서 출발하여 깊이 우선으로 탐색 후 노드를 방문한 순서대로 출력한다.
728x90
반응형