[Java]/Java 기초

[java] java 스레드(thread) - 멀티, 메인, 작업 / 동기화 메소드

기록자_Recordian 2025. 4. 10. 12:29
728x90
반응형
이전 내용

[java] java.lang 패키지

이전 내용 [java] 예외 (클래스, 처리), 다중 catch, 예외 떠넘기기, String args[]이전 내용 [java] 인터페이스 상속, 중첩 클래스, 중첩 인터페이스이전 내용 [java] instanceof, 인터페이스, 구현 클래스이

puppy-foot-it.tistory.com


프로세스

 
◆ 프로세스란?
프로세스는 실행 중인 프로그램을 의미한다. 각 프로세스는 독립적인 메모리 공간을 가지고 있으며, 운영 체제에서 할당된 자원을 포함한다. 프로세스는 멀티태스킹 환경을 지원하여 사용자 프로그램이 더 원활하게 실행될 수 있도록 하고, 각 프로세스는 자신만의 메모리 공간을 갖고 있어 안정성을 높인다.
 
하나의 애플리케이션은 멀티 프로세스를 만들기도 한다. 예) PDF 파일을 2개 실행 ▶ 2개의 PDF 프로세스 생성
 
[특징]

  • 자신만의 메모리 공간과 자원을 가진다.
  • 서로 다른 프로세스 간의 메모리 접근은 불가능하며, IPC(Inter-Process Communication)를 통해 통신한다.
  • 프로세스가 여러 개 동시에 실행될 수 있으며, 각각의 프로세스는 시스템 자원을 요청하고 사용할 수 있다.

[구성 요소]

  • 코드: 실행할 프로그램을 나타낸다.
  • 데이터: 프로그램이 사용하고 만들어낸 데이터.
  • 스택: 함수 호출, 로컬 변수 등을 포함하는 메모리 공간.
  • Heap: 동적으로 할당된 메모리 공간.
  • 프로세스 제어 블록 (PCB): 운영 체제가 각 프로세스를 관리하기 위해 필요한 정보 (프로세스 ID, 상태, 우선순위 등)를 저장.

[프로세스 상태]

  • 생성 (New): 프로세스가 생성되는 단계.
  • 실행 (Running): 프로세스가 CPU에서 실행 중인 상태.
  • 대기 (Waiting): 입출력 작업, 자원 요청 등으로 실행 대기 중.
  • 준비 (Ready): CPU를 할당받기 위해 대기 중인 상태.
  • 종료 (Terminated): 실행이 완료되어 종료된 상태.

스레드(Thread)

 
스레드는 프로세스 내에서 실행되는 가장 작은 실행 단위로, 하나의 프로세스는 여러 개의 스레드를 포함할 수 있고 모든 스레드는 프로세스의 자원을 공유한다. 스레드는 같은 프로세스 내에서 자원을 공유하기 때문에 메모리 사용이 효율적이며, 스레드 간의 전환이 프로세스 간 전환보다 빠르며, 필요한 계산을 병렬로 수행할 수 있다.
 
운영체제는 두 가지 이상의 멀티 태스킹 작업이 가능하도록 하기 위해 CPU 및 메모리 자원을 프로세스마다 적절히 할당해주고 , 병렬로 실행시킨다. 
 
[특징]

  • 같은 프로세스 내의 스레드는 메모리 공간과 자원을 공유.
  • 스레드는 경량(lightweight)털이를 가진다 ▶ 스레드 간의 전환이 프로세스 간 전환보다 빠르고 자원을 적게 소모.
  • 멀티스레드를 사용하면 동시에 여러 작업을 수행할 수 있다.

[구성 요소]

  • 스레드 ID: 유일한 식별자.
  • 스택: 각 스레드에 대해 고유한 함수 호출의 추적과 로컬 변수를 저장.
  • 프로그램 카운터: 다음에 실행할 명령어의 주소를 가리킴.
  • 레지스터: 각 스레드의 상태 저장을 위한 레지스터.
프로세스와 스레드

 
◆ 멀티 스레드
하나의 프로세스 내에서 여러 개의 스레드가 동시에 실행되는 것으로, 이는 한 프로그램이 동시에 여러 작업을 처리할 수 있게 한다.

  • 단일 스레드: 하나의 작업만 처리하므로, 사용자가 작업을 수행하는 동안 다른 작업은 대기해야 한다.
  • 멀티 스레드: 사용자가 입력하는 동안 데이터 처리와 다른 작업을 동시에 진행할 수 있다.

[멀티 스레드와 멀티 프로세스 비교]
멀티 프로세스는 운영체제에서 할당받은 자신의 메모리를 가지고 실행하기 때문에 각 프로세스는 서로 독립적이다. 따라서 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지 않는다.
하지만 멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 하나의 스래드가 예외를 발생시키면 프로세스 자체가 종료될 수 있어 다른 스레드에 영향을 미치게 된다. 
예를들어 멀티 프로세스인 워드와 엑셀을 동시에 사용히던 도중, 워드에 오류가 생겨 먹통이 되더라도 엑셀은 사용 가능하다. 그러나 멀티 스레드로 동작하는 메신저의 경우 파일을 전송하는 스레드에서 예외가 발생하면 메신저 프로세스 자체가 종료 되므로 채팅 스레드도 같이 종료된다. 그렇기 때문에 멀티 스레드에서는 예외 처리에 만전을 기해야 한다. 
 
◆ 메인 스레드
메인 스레드는 Java 애플리케이션에서 자동으로 생성되는 첫 번째 스레드이다. 모든 Java 프로그램은 메인 스레드에서 시작한다.
메인 스레드는 사용자 인터페이스와 주 로직을 전담하며, 프로그램의 흐름 제어, 추가 스레드를 생성 및 관리하는 역할을 한다. 또한, 메인 스레드가 종료되면 생성된 모든 하위 스레드도 함께 종료된다.
 
◆ 작업 스레드
작업 스레드는 특정 작업(예: 데이터 처리, 네트워크 요청 등)을 실행하는 스레드이다. 메인 스레드가 아닌 별도의 스레드로 동작한다. 작업 스레드는 작업을 분리하여 메인 스레드의 부하를 줄이고, 프로그램의 응답성을 향상시킨다. 작업 스레드는 일반적으로 스레드 풀에 저장되고, 필요할 때 재사용 된다.


작업 스레드 생성과 실행

 
멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로 스레드를 생성해야 한다.
Java에서는 작업 스레드를 구현하는 방법으로 주로 두 가지 방식, 즉 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있다. 작업 스레드는 생성시킨 후 start() 메서드를 호출해야만 실행된다.
 
1. Thread 클래스를 상속받아 작업 스레드 생성 (class 클래스명 extends Thread)
이 방식은 Thread 클래스를 직접 상속받아 새로 생성한 클래스에서 run() 메소드를 오버라이드하여 스레드에서 실행할 코드를 정의하는 방법이다.

public class WorkerThread extends Thread {
	@Override
    public void run() {
    	스레드가 실행할 코드;
    }
Thread thread = new WorkerThread();

 
코드를 절약하기 위해 Thread 익명 객체로 작업 스레드 객체를 생성할 수도 있다.

Thread thread = new Thread() {
	public void run() {
    	스레드가 실행할 코드;
    }
};

 
[단계]
1) Thread 클래스를 상속받는 새로운 클래스 정의:
Thread 클래스를 상속받은 클래스 내에서 run() 메소드를 오버라이드.
2 )스레드 객체 생성:
Thread 객체를 생성하고, 새로 정의한 클래스의 인스턴스를 사용.
3) 스레드 시작:
start() 메소드를 호출하여 스레드를 실행. 

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("작업 스레드에서 실행 중: " + i);
            try {
                Thread.sleep(500);  // 0.5초 동안 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();  // 작업 스레드 객체 생성
        thread.start();  // 작업 스레드 시작

        System.out.println("메인 스레드가 실행 중입니다.");
    }
}

2. Runnable 인터페이스를 구현하여 작업 스레드 생성(class 클래스명 implements Runnable)
이 방법은 Runnable 인터페이스를 구현한 클래스를 만들고, run() 메소드 내에서 실행할 코드를 정의한 다음, 이 객체를 Thread 클래스의 생성자로 사용하여 스레드를 생성하는 방법이다.
 
[Runnable]
Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 Runnable을 매개값으로 갖는 생성자를 호출해야 한다.

Thread thread = new Thread(Runnable target);

※ Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체이며, 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 한다. Runnable에는 run() 메소드 하나가 정의되어 있는데, 구현 클래스는 run()을 재정의해서 작업 스레드가 실행할 코드를 작성해야 한다.

class 클래스명 implements Runnable {
	public void run() {
    	스레드가 실행할 코드;
	}
}

 
또는 코드를 좀 더 절약하기 위해 Thread 생성자를 호출할 때 Runnable 익명 객체를 매개값으로 사용할 수 있다.

Thread thread = new Thread(new Runnable() {
	public void run() {
    	스레드가 실행할 코드;
    }
});

 
start() 메소드가 호출되면, 작업 스레드는 매개값으로 받은 Runnable의 run() 메소드를 실행하면서 작업을 처리한다.


[단계]
1) Runnable 인터페이스 구현:
Runnable 인터페이스를 구현하는 클래스를 정의하고, run() 메소드를 오버라이드.
2) 스레드 객체 생성:
Runnable 객체를 Thread 클래스의 생성자로 사용하여 새로운 Thread 객체 생성.
3) 스레드 시작:
start() 메소드를 호출하여 스레드 실행.

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("작업 스레드에서 실행 중: " + i);
            try {
                Thread.sleep(500);  // 0.5초 동안 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();  // Runnable 객체 생성
        Thread thread = new Thread(myRunnable);  // 스레드 객체 생성
        thread.start();  // 작업 스레드 시작

        System.out.println("메인 스레드가 실행 중입니다.");
    }
}

스레드 이름

 
스레드는 자신의 이름을 가지고 있다.

  • 메인 스레드: 'main'
  • 직접 생성한 스레드: 자동적으로 'Thread-n' (n은 번호)

만약 다른 이름으로 설정하고 싶다면 setName() 메소드로 변경하면 된다.

thread.setName("스레드 이름");

 
스레드 이름을 알고 싶은 경우에는 getName() 메소드를 호출하면 된다.

thread.getName();

 
만약 스레드 객체의 참조를 갖고 있지 않을 경우, currentThread()를 이용해서 현재 스레드의 참조를 얻을 수 있다.

Thread thread = Thread.currentThread();

 


동기화 메소드

 
◆ 동기화 메소드: 여러 스레드가 동시에 접근하지 못하도록 제어하여, 공유 자원에 대한 일관성을 유지하도록 돕는 메소드. synchronized 키워드를 사용하여 메소드나 블록을 정의한다. 이렇게 하면 해당 메소드가 실행 중인 동안 다른 스레드가 접근할 수 없게 된다.
스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없게 하려면 스레드 작업이 끝날 때까지 객체에 잠금을 걸어서 다른 스레드가 사용할 수 없도록 해야 한다. 자바는 임계 영역을 지정하기 위해 동기화 메소드를 제공한다.
스레드가 객체 내부의 동기화 메소드를 실행하면 즉시 객체 잠금을 걸어 다른 스레드가 동기화 메소드를 실행하지 못하도록 한다.
※ 임계 영역: 멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역.
 
동기화 메소드는 메소드 전체 내용이 임계 영역이므로 스레드가 동기화 메소드를 실행하는 즉시 객체에는 잠금이 일어나고, 스레드가 동기화 메소드를 실행 종료하면 잠금이 풀린다.
 
동기화 메소드는 여러 스레드의 접근을 제어하여 데이터의 일관성을 보장하나, 스레드 간의 대기를 유발할 수 있어 전체 성능을 저하시킬 수 있고, 교착 상태에 빠질 수 있다.

public class Calculator {
	private int memory;

	public int getMemory() {
		return memory;
	}

	public void setMemory(int memory) { // 계산기 메모리에 값 저장
		this.memory = memory; // 매개값을 memory 필드에 저장
		try {
			Thread.sleep(200); // 스레드를 2초간 일시 정지
		} catch (InterruptedException e) {
		}
		// 스레드 이름 얻기 + 메모리 값
		System.out.println(Thread.currentThread().getName() + ": " + this.memory);
	}
}

 

public class User1 extends Thread {
	private Calculator calculator;

	public void setCalculator(Calculator calculator) {
		this.setName("User1"); // 스레드 이름 설정
		this.calculator = calculator; // 공유 객체인 calculator 필드에 저장
	}

	public void run() {
		calculator.setMemory(100); // 공유 객체의 메모리에 특정값 저장
	}
}

public class User2 extends Thread {
	private Calculator calculator;

	public void setCalculator(Calculator calculator) {
		this.setName("User2");
		this.calculator = calculator;
	}

	public void run() {
		calculator.setMemory(50);
	}
}

 

public class MainThreadExample {

	public static void main(String[] args) {
		Calculator calculator = new Calculator();

		User1 user1 = new User1(); // user1 스레드 생성
		user1.setCalculator(calculator); // 공유 객체 설정
		user1.start(); // user1 스레드 시작

		User2 user2 = new User2(); // user2 스레드 생성
		user2.setCalculator(calculator); // 공유 객체 설정
		user2.start(); // user2 스레드 시작

	}
}

▶ User1 스레드가 Calculator 객체의 memory 필드에 100을 먼저 저장하고 2초간 일시 정지 상태가 된다. 그동안에 User2 스레드가 memory 필드값을 50으로 변경한다. 2초가 지나 User1 스레드가 다시 실행 상태가 되어 memory 필드값을 출력하면 User2 스레드가 저장한 50이 출력된다. (반대로 둘 다 100이 나올 수도 있다) 이 현상은 경합 조건으로, 두 스레드가 동시에 동일한 자원에 접근할 때 발생하는 문제다.

 
이렇게 되면 멀티 스레드 프로그램에서 스레드들이 객체를 공유해서 작업해야 하는 경우 User1이 사용하던 객체를 User2가 상태를 변경하여 User1이 의도했던 것과는 다른 결과를 산출하게 된다.
이를 위해 동기화 메소드를 만드는데, 동기화 메소드를 만드려면 메소드 선언에 synchronized 키워드를 붙이면 된다.
즉, User1과 User2의 값이 동일하게 나오도록 하려면, Calculator 클래스의 setMemory() 메소드에 synchronized 키워드를 붙여주면 된다.

public synchronized void setMemory(int memory) {

 
다시 MainThreadExample 

 

★ 동기화 메소드의 필요성에 대한 예
은행 계좌가 있고, 여러 사람이 동시에 이 계좌의 잔액을 조회하거나 출금하는 상황을 고려해 본다. 이 계좌는 공유 자원이며, 여러 스레드(고객)가 동시에 접근할 수 있는 환경이다.

[계좌 잔액]

  • 초기 잔액: 은행 계좌의 잔액이 1000원이라고 가정.
  • 고객1의 출금 요청: 고객1이 계좌에서 700원을 출금 원함.
  • 고객2의 출금 요청: 고객2도 동시에 같은 계좌에서 800원을 출금 원함.
동기화가 없다면, 두 고객이 동시에 출금 요청을 처리하게 된다.
고객1이 계좌 잔액을 읽고 1000원을 확인한 후, 700원을 출금하겠다고 결정하고, 동시에 고객2도 계좌 잔액을 읽고 1000원을 확인한 후, 800원을 출금하겠다고 결정한다.

이렇게 될 경우,
고객1 출금: 1000-700 = 잔액 300원
고객2 출금: 1000-800 = 잔액 200원

하지만, 실제로는 고객1이 출금하고 남은 잔액은 300원이기 때문에 고객2의 출금 요청은 처리될 수 없지만, 시스템은 1000원에서 800원을 출금할 수 있다고 판단하게 된다. 이로 인해 출금이 진행되고 잔액은 -500원이 되는 상황이 발생한다.

이런 문제를 방지하기 위해 동기화 메소드를 사용하여, 계좌 잔액에 접근할 때 한 번에 하나의 고객만 접근할 수 있도록 해야 한다.

▶ 고객1과 고객2가 동시에 출금 요청을 하더라도, 한 번에 하나의 메소드만 접근할 수 있도록 처리하여 고객1이 출금을 처리하는 동안 고객2는 대기하고, 고객1의 요청이 완료된 후에 고객2가 실행되어야 하므로 올바른 잔액을 기준으로 출금 처리가 이뤄진다.
이렇게 되면 동기화를 통해 안전하게 자원을 관리함으로써 예기치 못한 오류나 불일치를 방지할 수 있다.

예제1

 
Q. 멀티 스레딩 구현해보기
멀티 스레딩을 활용하여 한 스레드에서 "안녕하세요!"를 5번 출력하고, 메인 스레드는 독립적으로 종료되면서 "메인 쓰레드 종료"라는 메시지가 출력되도록 구현
 
[HelloTask 클래스]
HelloTask 클래스는 Runnable 인터페이스를 구현한다. 이는 쓰레드에서 실행될 작업을 정의하는 클래스이다.
run 메서드 내의 for 루프를 통해 "안녕하세요!"라는 메시지를 5번 출력한다.

public class HelloTask implements Runnable {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("안녕하세요!");
		}
	}
}

 
[HelloMain 클래스]
- Runnable 타입의 threadHello 변수를 HelloTask의 인스턴스로 초기화
- 새로운 Thread 객체를 생성하고, 이를 threadHello로 초기화한다. 이렇게 생성된 스레드는 HelloTask의 run 메서드를 실행
- thread.start() 메서드를 호출하면 새로운 스레드가 시작되고, 이 스레드는 "안녕하세요!"를 출력
- 마지막으로 "메인 쓰레드 종료"라는 메시지가 출력

public class HelloMain {

	public static void main(String[] args) {
		Runnable threadHello = new HelloTask();
		Thread thread = new Thread(threadHello);
		thread.start();

		System.out.println("메인 쓰레드 종료");
	}
}

 
[Runnable 익명 구현 객체]
익명 클래스를 사용하여 스레드를 생성하고 실행
※ 익명 클래스: 클래스 이름 없이 바로 구현된 클래스

public class HelloPrint {

	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 5; i++) {
					System.out.println("안녕하세요!");
				}
			}
		});
		thread.start();

		System.out.println("메인 쓰레드 종료");
	}
}

- Thread 객체를 생성하면서 Runnable 인터페이스의 익명 클래스를 구현
- Runnable의 run 메서드를 오버라이드하여, 이 메서드 내에서는 for 루프를 통해 "안녕하세요!"라는 메시지를 5번 출력
- thread.start() 메서드를 호출하여 새로운 스레드를 시작합니다. 이 메서드를 호출하면 Runnable로 정의된 run 메서드가 실행
- 새로운 스레드가 실행되는 동안 메인 스레드는 "메인 쓰레드 종료"라는 메시지를 출력
▶ 익명 클래스를 사용하면 코드가 간결해지고, 클래스 선언 없이 직접 구현할 수 있어 편리하다.


예제2

 
Q. 스레드 간 동기화
두 개의 스레드가 공유 변수 count를 각각 100번씩 증가시키는 프로그램 작성.
- synchronized 키워드를 사용해 스레드 간 경합 조건 방지
 
[필자 답 - 보완 필요]

public class Counter {
	private int sum = 0;

	public int getCount() {
		return sum;
	}

	public synchronized void incrementCount() {
		for (int i = 0; i < 100; i++) {
			sum++;
			System.out.print(Thread.currentThread().getName() + "에서 실행 중 - ");
			System.out.print("쓰레드 이름: " + Thread.currentThread().getName());
			System.out.println(", 쓰레드 id: " + Thread.currentThread().getId());
		}
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
		System.out.println("sum =>" + sum);
	}

	public void run() {
		incrementCount();
	}
}
public class Thread1 extends Thread {
	private Counter counter;

	public void setCount(Counter counter) {
		this.setName("MyThread1");
		this.counter = counter;
	}

	public void run() {
		counter.incrementCount();
	}
}
public class Thread2 extends Thread {
	private Counter counter;

	public void setCount(Counter counter) {
		this.setName("MyThread2");
		this.counter = counter;
	}

	public void run() {
		counter.incrementCount();
	}
}
public class MainCount {

	public static void main(String[] args) {
		System.out.println("메인 쓰레드에서 실행 중 - 쓰레드 이름: main, 쓰레드 ID: 1");

		Counter counter = new Counter();

		Thread1 thread1 = new Thread1();
		thread1.setCount(counter);
		thread1.start();

		Thread2 thread2 = new Thread2();
		thread2.setCount(counter);
		thread2.start();

	}
}

▶ MyThread1과 MyThread2가 서로 왔다갔다 하면서 진행이 돼야 하는데, MyThread1이 모두 진행 된다음 MyThread2가 진행된다.
 
[교수님 답을 토대로 보완]
추가 사항
- Main 스레드에는 스레드의 상태를 출력하는 printThreadInfo() 추가

public class Counter {
	private int sum;

	public int getCount() {
		return sum;
	}

	public synchronized void incrementCount() {
		sum++;
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
	}
}
public class Thread1 extends Thread {
	private Counter counter;

	public void setCount(Counter counter) {
		this.setName("MyThread1");
		this.counter = counter;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			counter.incrementCount();
			MainCount.printThreadInfo("쓰레드1");
		}
		System.out.println("sum1 =>" + counter.getCount());
	}
}

public class Thread2 extends Thread {
	private Counter counter;

	public void setCount(Counter counter) {
		this.setName("MyThread2");
		this.counter = counter;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			counter.incrementCount();
			MainCount.printThreadInfo("쓰레드2");
		}
		System.out.println("sum2 =>" + counter.getCount());
	}
}

▶ 기존에는 Counter 클래스에 for 반복문을 넣었었는데, 이번에는 그렇게 하지 않고 Thread1과 Thread2 클래스에 각각 오버라이드 하는 방식으로 변경하였다.
 

public class MainCount {

	public static void main(String[] args) {
		System.out.println("메인 쓰레드에서 실행 중 - 쓰레드 이름: main, 쓰레드 ID: 1");

		Counter counter = new Counter();

		Thread1 thread1 = new Thread1();
		thread1.setCount(counter);
		Thread2 thread2 = new Thread2();
		thread2.setCount(counter);

		thread1.start();
		thread2.start();

		// 스레드 이름 설정
		thread1.setName("MyThread1");
		thread2.setName("MyThread2");
	}

	// 현재 스레드 정보 출력 메소드
	public static void printThreadInfo(String context) {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();
		long threadId = currentThread.getId();
		System.out.print(context + "에서 실행 중 - 스레드 이름: ");
		System.out.println(threadName + ", 스레드 id" + threadId);
	}
}

▶ 추가 사항인 메인 함수에 printThreadInfo() 메소드를 추가해 주었다.

 
[추가]
- 스레드1, 2는 익명 클래스 사용하여 스레드를 생성하고 실행

public class MainCountVersion2 {
	private static int count = 0;

	public static void main(String[] args) {
		// 메인 스레도 정보 출력
		printThreadInfo("메인 스레드");

		// 두 개의 스레드 생성
		Thread thread1 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					printThreadInfo("스레드1");
					increment();
				}
				System.out.println("sum1 =>" + count);
			}
		});
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					printThreadInfo("스레드2");
					increment();
				}
				System.out.println("sum2 =>" + count);
			}
		});
		thread1.start();
		thread2.start();
	}

	public static synchronized void increment() {
		count++;
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}
	}

	// 현재 스레드 정보 출력 메소드
	public static void printThreadInfo(String context) {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();
		long threadId = currentThread.getId();
		System.out.print(context + "에서 실행 중 - 스레드 이름: ");
		System.out.println(threadName + ", 스레드 id" + threadId);
	}
}

[참고]
혼자 공부하는 자바

 


다음 내용

[java] 스레드 제어

이전 내용 [java] java 스레드(thread) - 멀티, 메인, 작업 / 동기화 메소드이전 내용 [java] java.lang 패키지이전 내용 [java] 예외 (클래스, 처리), 다중 catch, 예외 떠넘기기, String args[]이전 내용 [java] 인

puppy-foot-it.tistory.com

 

728x90
반응형