spring
1.0.0
1.0.0
  • README
  • Git
    • Basic
    • Remote Repository
    • Log & Diff
    • Rebase&Cherri-Pick
    • git-flow
  • DevOps
    • Monolithic vs MSA
    • Jenkins 시작하기
    • Airflow 시작하기
    • Airflow 시작하기
    • Build Tools
      • maven
  • 개발 방법론
    • TDD
  • Spring
    • IoC
    • Is Spring Bean Thread-Safe?
    • Spring Singleton
    • Component Scan
    • Spring Annotation
    • 의존 관계 주입(DI)
    • Lombok 활용하기
    • Bean 생명주기와 콜백
    • Bean Scope
    • AOP(1) - AOP란
    • AOP(2) - Aop Proxy
    • AOP(3) - Dynamic Proxy
    • AOP(4) - AspectJ
    • POJO
    • Spring 서비스 구조
    • Transaction
    • JPA란?
    • JPA Entity
    • Spring Data JPA
    • Spring Data Specification
    • Model Mapping
    • Cache
    • restTemplate
    • YAML 파일 설정
    • Spring Boot
      • H2 DB 설정
      • 다중 데이터베이스 설정
      • Mybatis 연동하기
    • Spring Batch
      • Batch 시작해보기
      • Batch Job Flow
      • Job
      • Step
      • Batch Scope & Job Parameter
      • JobRepository와 메타테이블
      • Chunk 지향 프로그래밍
      • ItemReader
      • ItemProcessor
      • ItemWriter
      • Batch Schedular
      • Job별 Bean등록하기
      • Batch 구현시 발생한 오류 정리
      • Spring Batch Scaling
        • Multithread Job구현시 이슈사항
    • Spring test
      • Junit5
        • 테스트 이름 표기
        • 테스트 그룹 사이의 관계
        • 태그와 필터링
        • 동적 테스트
        • 테스트 LifeCycle
        • 테스트 메서드
        • 테스트 순서
        • AssertJ
        • 테스트 병렬 실행
        • AssertJ
        • Mock
      • Spring Boot Test DB 분리
      • Spring Batch Test
  • Web Application
    • Web Server & WAS
    • 관련 개념 - HTTP API, HTML, CSR, SSR
    • Servlet
    • JSP
    • Cookie And Session
    • 예외페이지
    • Java Bean
    • JDBC
    • Connection Pool
    • 파일 업로드
    • Expression Language
    • JSTL
    • FrontController패턴 Command 패턴
    • Forwarding
    • MVC
    • 회원가입예제
    • 참고
      • 개발환경설정
  • Java+
    • SOAP/WSDL vs REST
    • WSDL을 JAVA로 변환하기
    • SOAP 통신 OPEN API로 개발해보기
  • Java
    • Basic
      • 변수와 타입
      • 연산자
      • 조건문과 반복문
      • 참조 타입
      • 클래스
      • 상속(Inheritance)
      • 인터페이스(Interface)
      • 중첩 클래스와 중첩 인터페이스
      • 예외 처리
      • API - Object, System, Class, Math, Wrapper
      • API - String, StringBuffer, StringBuilder
      • Thread
      • Generic
      • Lambda
      • Collection - List, Set
      • Collection - Map
      • Collection - Tree
      • Collection - Stack, Queue
      • Stream
      • Reflection
      • 정규표현식
      • GUI
      • UML
      • Serializable
    • Advanced
      • OutOfMemoryError
      • AutoValue
      • meta-annotation
        • @Retention
        • @Target
        • @Repeatable
    • Effective Java 3/E
      • ITEM 1: Static Factory Method(정적 메소드)
      • ITEM 2: Builder Pattern
      • ITEM 3: Singleton
      • ITEM 4: Private Constructor
      • ITEM 5: Dependency Injection
      • ITEM 6: Avoid Unnecessary Object
      • ITEM 7: Eliminate Object Reference
      • ITEM 8: Avoid finalizer and cleaner
      • ITEM 9: try-with-resources
      • ITEM 10: The gerneral contract when overriding equlas
      • ITEM 11: Overriding hashCode
      • ITEM 12: overriding toString
      • ITEM 13: overriding clone judiciously
      • ITEM 14: Consider implementing comparable
      • ITEM 15: 클래스와 멤버의 접근을 최소화해라
      • ITEM 16: Use Accessor methods
      • ITEM 17: 변경 가능성을 최소화해라(불변 클래스)
      • ITEM 18: 상속보단 컴포지션을 사용해라
      • ITEM 19: 상속을 고려해 설계하고 문서화해라
      • ITEM 20: 추상 클래스보다 인터페이스를 우선하라
      • ITEM 21: 인터페이스는 구현하는 쪽을 생각해 설계해라.
      • ITEM 22: 인터페이스는 타입을 정의하는 용도로만 사용해라
      • ITEM 23: 태그 달린 클래스보다 클래스 계층구조를 활용해라
      • ITEM 24: 멤버 클래스는 되도록 static으로 구현해라
      • ITEM 25: 톱레벨 클래스는 한 파일에 하나만 생성해라.
      • ITEM 26: Raw type은 사용하지 마라
      • ITEM 27: 비검사 경고를 제거해라
      • ITEM 28: 배열보다는 리스트를 사용해라
      • ITEM 29: 이왕이면 제네릭 타입으로 만들어라
      • ITEM 30: 이왕이면 제네릭 메서드로 만들어라
      • ITEM 31 : 한정적 와일드카드를 사용해 API 유연성을 높여라
      • ITEM 32: 제네릭과 가변인수를 함께 쓸 때는 신중해라
      • ITEM 33: 타입 안전 이종 컨테이너를 고려해라
      • ITEM 34: int 상수 대신 열거 타입을 사용해라
      • ITEM 35: ordinal 메서드 대신 인스턴스 필드를 사용해라
      • ITEM 36: 비트 필드 대신 EnumSet을 사용해라
      • ITEM 37: ordinal 인덱싱 대신 EnumMap을 사용해라
      • TEM 38 : 확장할 수 있는 열거타입이 필요하면 인터페이스를 사용해라
      • ITEM 39: 명명 패턴보다 애너테이션을 사용해라
      • ITEM 40: @Override 어노테이션을 일관되게 사용해라
      • ITEM 41: 정의하려는 것이 타입이라면 마커 인터페이스를 사용해라
      • ITEM 42: 익명 클래스보다는 람다를 사용해라
      • ITEM 43: 람다보다는 메서드 참조를 사용해라
      • ITEM 44: 표준 함수형 인터페이스를 사용해라
      • ITEM 45: 스트림은 주의해서 사용해라
      • ITEM 46: 스트림에서 부작용 없는 함수를 사용해라
      • ITEM 47: 반환 타입으로는 스트림보다 컬렉션이 낫다.
      • ITEM 48: 스트림 병렬화는 주의해서 사용해라
      • ITEM 49: 매개변수가 유효한지 검사해라
      • ITEM 50: 적시에 방어적 복사본을 만들어라
      • ITEM 51: 메서드 시그니처를 신중히 설계해라
      • ITEM 52: 다중정의는 신중히 사용해라
      • ITEM 53: 가변인수는 신중히 사용해라
      • ITEM 54: null이 아닌, 빈 컬렉션이나 배열을 반환해라
      • ITEM 55: Optional 반환은 신중하게 해라
      • ITEM 56: 공개된 API 요소에는 항상 주석을 작성해라
      • ITEM 57: 지역변수의 범위를 최소화해라
      • ITEM 58: 전통적인 for 문보다는 for-each문을 사용해라
      • ITEM 59: 라이브러리를 익히고 사용해라
      • ITEM 60: 정확한 답이 필요하다면 float와 double은 피해라
      • ITEM 61: 박싱된 기본 타입보다는 기본 타입을 사용해라
      • ITEM 62: 다른 타입이 적절하다면 문자열 사용을 피해라
      • ITEM 63: 문자열 연결은 느리니 주의해라
      • ITEM 64: 객체는 인터페이스를 사용해 참조해라
      • ITEM 65: 리플렉션보다는 인터페이스를 사용해라
      • ITEM 66: 네이티브 메서드는 신중히 사용해라
      • ITEM 67: 최적화는 신중히 해라
      • ITEM 68: 일반적으로 통용되는 명명 규칙을 따라라
    • 객체지향 설계 원칙(SOLID)
    • 디자인패턴
      • Strategy Pattern
      • Template Method Pattern
      • Factory Method Pattern
      • Singleton
      • Delegation
      • Proxy
      • Adapter Pattern
    • 실습
      • 인터페이스 실습 - Vehicle
      • 인터페이스 실습 - Remote
      • GUI 실습 - Calculator
      • GUI 실습 - button
      • GUI 실습 - lotto
      • Thread 실습 - 좌석예약, 메세지보내기
    • Jar vs War
  • 데이터베이스
    • KEY
    • Index
    • Transaction
    • Trigger
    • Procedure / Function
    • Package
    • 데이터베이스 배움터
      • 데이터베이스 시스템
      • 관계데이터 모델
      • 관계대수와 SQL
    • MySQL
      • Database란
      • MySQL 시작하기
      • MySQL Database
      • MySQL Table
      • CRUD
      • 관계형 데이터베이스
      • Server와 Client
    • PostgreSQL
    • NoSQL
      • Install Cassandra on mac
      • Cassandra란?
      • NiFi란
  • Algorithm
    • String
    • Recursion
    • Dynamic Programming
    • Array, Struct, Pointer
    • Math
    • Sort
    • List
    • Stack
    • Queue
    • Graph
    • Tree
    • Maze
    • AVL
    • 이진탐색트리(Binary Search Tree)
    • DFS와 BFS
    • 다익스트라 알고리즘(Dijkstra's Algorithm)
    • Red-Black 트리
    • A* 알고리즘
    • Heap
    • Huffman Coding
    • Priority Queue
    • Bellman-Ford 알고리즘
    • C++
      • Class
      • STL
        • STL pair
        • STL Container - Associate Container
        • STL Container - Sequence Container
        • STL Container - Container Adapter
  • JavaScript
    • JABASCRIPT BASIC
    • Shallow Copy vs Deep Copy
    • OBJECT MODEL
    • NODE
    • 동기 처리 vs 비동기 처리
    • AJAX
    • CALLBACK
    • PROMISE
    • DEFERRER
    • UNDERSCORE
    • WEBPACK
    • SCOPE
    • EXECUTION CONTEXT
    • Image Object
    • BFCache란?
    • history.scrollRestoration
    • Intersection Observer
    • JWT - JSON Web Token
    • HTML vs JSON
  • Vue.js
    • 환경설정
    • Vue.js란?
    • Vue Instance
    • Vue Component
    • Vue Router
    • HTTP 통신
    • Template
    • Single File Component
    • Vue Animation
    • Vuex
    • Djnago와 연동하기
  • Backbone.js
    • Model
    • Collection
    • Sync
    • view
  • Node.js
    • Doit! - 노드로 만들 수 있는 대표적인 서버와 용도
    • Doit! - 노드에 대해 알아보고 개발 도구 설치하기
    • Doit! - 노드 간단하게 살펴보기
    • Doit! - 노드의 자바스크립트와 친해지기
    • Doit! - 노드의 기본 기능 알아보기
    • Doit! - 웹 서버 만들기
    • Doit! - 데이터베이스 사용하기
    • Doit! - 익스프레스 프로젝트를 모듈화하기
    • Doit! - 뷰 템플릿 적용하기
    • Doit! - 패스포트로 사용자 인증하기
    • Doit! - 채팅서버 만들기
    • Doit! - JSON-RPC 서버 만들기
  • Python
    • Warning-Could not import the lzma module
    • Pandas
      • Pandas 자료구조
      • Pandas 데이터 입출력
      • DataFrame Data 살펴보기
      • 시각화 도구 - Matplotlib
  • ML
    • 추천 시스템
      • Collaborative Filtering
      • Matrix Factorization
  • Django
    • Basic
      • 환경설정
      • About Django
      • Start Django Project
      • Secret Key 관리하기
      • Settings 분리하기
      • Django App
      • Django View & URL (1)
      • Django Model
        • MySQL 연동
      • Django Admin
      • Django View & URL (2)
      • Django Template
      • Django Template & View & URL
      • Django Static
      • Django form
    • Advanced
      • Django Generic View
      • Django Automated Testing
      • Django Extenstion Template
      • Django Model Package
      • Django OpenSSL setting
    • REST framework
      • Rest API
      • Serializers
      • ViewSet
    • Error
      • 환경설정 zlib 오류발생
      • ModuleNotFoundError
    • 패키지
      • django-debug-toolbar
    • Vue.js 연동하기
  • Ruby
    • variable & input/output
    • 조건문
    • 반복문
    • Array & Hash
    • Method
    • Proc&Lamda
    • Class
  • Ruby on Rails
    • Scaffolding
    • Controller
    • Model
    • Model-M:N relation
    • Model Validation
    • 멋사 10주차 수업(Tip)
  • HTML/CSS
    • Udacity - Intro to HTML/CSS
    • Udacity - Responsive Web Design
    • Udacity - Responsive Images
    • HTML Basic
    • CSS Basic
    • HTML5 Sementic Tag
    • HTML 텍스트 관련 태그들
    • HTML5 멀티미디어
    • HTML 폼 관련 태그들
    • 텍스트 관련 스타일
    • 색상과 배경을 위한 스타일
    • 레이아웃을 위한 스타일
    • CSS 포지셔닝
    • 다재다능한 CSS3 선택자
    • CSS와 애니메이션
    • 반응형 웹이란?
  • OS(운영체제)
    • Linux
      • Daemon
      • Cron
      • 프로세스 관련 명령어
      • 텍스트 파일 명령어
  • Network
    • 네트워크 기본 개념
    • 네트워크 기본 규칙
    • 물리 계층
    • 데이터 링크 계층
    • 네트워크 계층
    • 전송 계층
    • 응용 계층
    • 네트워크 전체 흐름
    • 무선 랜
  • IT 기타지식
    • NAS란
Powered by GitBook
On this page
  • 개념
  • 프로세스와 스레드
  • 메인 스레드
  • 작업 스레드 생성과 실행
  • Thread 클래스로부터 직접 생성
  • Thread 하위 클래스로부터 생성
  • 스레드 이름
  • 스레드 우선순위
  • 동기화 메소드와 동기화 블록
  • 공유 객체를 사용할 때 주의할 점
  • 동기화 메소드 및 동기화 블록
  • 스레드 상태
  • getState()
  • 스레드 상태 제어
  • 상태 제어 메소드

Was this helpful?

  1. Java
  2. Basic

Thread

PreviousAPI - String, StringBuffer, StringBuilderNextGeneric

Last updated 3 years ago

Was this helpful?

개념

프로세스와 스레드

  • 프로세스 : 운영체제에서 실행 중인 하나의 애플리케이션

사용자가 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션 코드를 실행하는데 이것을 프로세스라한다. 하나의 애필리케이션은 다중 프로세스를 만들기도 한다.

예를들어 Chrome 브라우저를 두개 실행하면 두 개의 Chrome 프로세스가 생성된 것이다.

  • 멀티 태스킹(multi tasking) : 두 가지 이상의 작업을 동시에 처리하는 것

운영체제는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다 적절하게 할당해주고, 병렬로 실행시킨다. 멀티 태스킹은 꼭 멀티 프로세스를 의미하지는 않는다. 한 프로세스 내에서도 멀티 태스킹을 할 수 있다. 예) 메신저 - 파일전송, 채팅 기능 동시 수행

  • 멀티 스레드(multi thread) : 하나의 프로세스 내에서의 멀티 태스킹

멀티 프로세스는 운영체제에서 할당받은 자신의 메모리를 가지고 실행하기 때문에 서로 독립적이다. 따라서 하나의 프로세스에서 오류가 발생해도 다른 프로세스에 영향을 미치지 않는다.

멀티 스레드는 하나의 프로세스 내부에 생성되기 때문에 하나의 스레드가 예외를 발생시키면 프로세스가 종료될 수 있어 다른 스레드에 영향을 미친다. 그렇기 때문에 멀티 스레드에서는 예외처리를 신경써야한다.

메인 스레드

모든 자바 애플리케이션은 메인 스레드(main thread)가 main() 메소드를 실행하면서 시작된다. 메인 스레드는 main() 메소드의 첫 코드부터 아래로 순차적으로 실행하고,main()의 마지막 코드를 실행하거나 return 문을 만나면 종료된다.

메인 스레드는 필요에 따라 작업 스레드를 생성해 병렬로 코드를 실행할 수 있다. 즉, 멀티 스레드를 생성해 멀티 태스킹을 수행한다. 싱글 스레드 어플리케이션에서는 메인 스레드가 종료되면 프로세스도 종료된다. 하지만 멀티 스레드 애플리케이션에서는 실행 중인 스레드가 하나라도 있다면, 프로세스는 종료되지 않는다.

작업 스레드 생성과 실행

멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고, 각 작업별로 스레드를 생성해야한다. 자바에서는 작업 스레드로 객체로 생성되기 때문에 클래스가 필요하다.

  • java.lang.Thread 클래스를 직접 객체화해서 생성

  • Thread를 상속해 하위 클래스로 생성

Thread 클래스로부터 직접 생성

java.lang.Thread 클래스를 직접 객체화해서 생성하려면 Runnable 을 매개값으로 갖는 생성자를 호출해야한다.

Thread thread = new Thread(Runnable target);

Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름이다. Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어서 대입해야한다. Runnable은 인터페이스 타입이므로 구현 객체를 만들어야한다. Runnable 에는 run() 메소드가 정의되어있다.

class Task implements Runnable{
	@Override
    public void run(){
        // 스레드가 실행할 코드;
    }
}
Runnable task = new Task();
Thread thread = new Thread(task);

Runnable은 작업 내용을 가지고 있는 객체이지 실제 스레드는 아니다. 그러므로 Runnable 구현 객체를 생성한 후에 이것을 매개값으로 해서 Thread 생성자를 호출하면 작업 스레드가 생성된다.

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

오히려 이 방법이 더 많이 사용된다.

Thread thread = new Thread(()->{
        //스레드가 실행할 코드;
});

Runnable 인터페이스는 run()메소드 하나만 정의 되어있기 때문에 함수적 인터페이스이다. 따라서 다음과 같이 람다식을 매개값으로 사용할 수 있다.(자바 8부터 지원)

작업 스레드는 생성되는 즉시 실행되지 않는다. start() 메소드를 호출해야만 실행된다.

thread.start();

예제

0.5 초 주기로 beep음을 발생시키면서 동시에 프린팅하는 작업

package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Toolkit tk = Toolkit.getDefaultToolkit();
		for(int i=0;i<5;i++) {
			tk.beep();
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				// 0.5초마다 비프음 발생
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}
}

이렇게 하는 경우 Beep음이 발생한 후 "띵"이라는 글자가 print 된다. 동시에 작업을 하려면 두 작업중 한개를 메인 스레드가 아닌 다른 스레드에서 실행시켜줘야한다.

  • 방법1

package chap12;

import java.awt.Toolkit;

public class BeepTask implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		Toolkit tk = Toolkit.getDefaultToolkit();
		for(int i=0;i<5;i++) {
			tk.beep();
			try {
				// 0.5초마다 비프음 발생
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}

}
package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Runnable beepTask = new BeepTask();
		Thread thread = new Thread(beepTask);
		thread.start();
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}

}
  • 방법2

package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				Toolkit tk = Toolkit.getDefaultToolkit();
				for(int i=0;i<5;i++) {
					tk.beep();
					try {
						Thread.sleep(500);
					} catch (Exception e) {
					}
				}
			}
		});
		thread.start();
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				Thread.sleep(500);
			} catch (Exception e) {
			}
			
		}
	}

}
  • 방법3

package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {

		Thread thread = new Thread(()-> {
			Toolkit tk = Toolkit.getDefaultToolkit();
			for(int i=0;i<5;i++) {
				tk.beep();
				try {
					Thread.sleep(500);
				} catch (Exception e) {
				}
			}
		});

		thread.start();
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}

}

Thread 하위 클래스로부터 생성

작업 스레드가 실행할 작업을 Runnable로 만들지 않고, Thread 하위 클래스로 작업 스레드를 정의하면서 작업 내용을 포함할 수 있다.

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

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

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

이렇게 생성한 스레드에 .start() 를 해주면 작업 스레드는 자신의 run() 메소드를 실행한다.

예제

package chap12;

import java.awt.Toolkit;

public class BeepThread extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		Toolkit tk = Toolkit.getDefaultToolkit();
		for(int i=0;i<5;i++) {
			tk.beep();
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
}
package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {

		Thread thread = new BeepThread();
		thread.start();
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}

}
  • 방법2

package chap12;

import java.awt.Toolkit;

public class BeepPrintEx {

	public static void main(String[] args) {
		Thread thread = new Thread(){
            	public void run() {
                    // TODO Auto-generated method stub
                    Toolkit tk = Toolkit.getDefaultToolkit();
                    for(int i=0;i<5;i++) {
                        tk.beep();
                        try {
                            // 0.5초마다 비프음 발
                            Thread.sleep(500);
                        } catch (Exception e) {
                            // TODO: handle exception
                        }
                    }
                }            
        }
		thread.start();
		for(int i=0;i<5;i++) {
			System.out.println("띵");
			try {
				// 0.5초마다 비프음 발
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}
}

스레드 이름

스레드는 자신의 이름을 가지고 있다. 큰 역할을 하는 것은 아니지만, 디버깅할 때 어떤 스레드가 어떤 작업을 하는지 확인할 때 사용된다. 메인 스레드는 "main"이라는 이름을 가지고 있고, 우리가 직접 생성한 스레드는 자동적으로 "Thread-n" 로 이름이 설정된다. 만약 다른 이름으로 설정하고 싶다면 setName() 메소드로 변경할 수 있다.

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

반대로 스레드 이름을 알고 싶다면 getName() 으로 받아올 수 있다.

thread.getName();

setName()과 getName()은 Thread 인스턴스 메소드여서 스래드 객체의 참조가 필요하다. 스레드 객체를 참조하고 있지 않다면 currentThread() 정적메소드로 참조를 얻을 수 있다.

Thread thread = Thread.currentThread();

스레드 우선순위

스레드는 동시성(concurrency) 또는 병렬성(parallelism)으로 실행된다.

  • 동시성 : 멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아가며 실행되는 성질

  • 병렬성 : 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질

스레드 수가 코어의 수보다 많을 경우에 스레드를 어떤 순서에 의해 동시성으로 실행할 것인지 결정해야한다. 이것을 스레드 스케쥴링이라 한다. 스레드 스케줄링에 의해 스레드들은 아주 짧은 시간에 번갈아가면서 run() 메소드를 조금씩 수행한다.

자바에서 스레드 스케줄링은 우선순위(Priority) 방식과 순환 할당(Round-Robin) 방식을 사용한다.

  • 우선순위 : 우선순위가 높은 스레드가 실행 상태를 더 많이 가지도록 스케줄링 한것

  • 순환 할당 : 시간 할당량(Time Slice)을 정해서 하나의 스레드를 정해진 시간만큼 실행하고 다시 다른 스레드를 실행하는 방식

우선 순위 방식은 스래드 객체에 우선순위 번호를 부여할 수 있기때문에 개발자가 코드로 제어할 수 있으나, 순환 할당 방식은 자바 가상 기계에 의해서 정해지기 때문에 코드로 제어할 수 없다.

우선순위 방식에서 우선순위는 1(가장 낮은 우선순위)~10(가장 높은 우선순위)까지 부여된다. 우선순위를 부여하지 않으면 모든 스레드들은 기본적으로 5의 우선순위를 할당받는다.

thread.setPriority(우선순위);

setPriority() 메소드를 이용해서 우선순위를 줄 수 있다. 1~10의 값을 직접 주어도 되지만, 코드의 가독성을 높이기 위해 클래스 상수를 사용할 수도 있다.

thread.setPriority(Thread.MAX_PRIORITY);	// 10
thread.setPriority(Thread.NORM_PRIORITY);	// 5
thread.setPriority(Thread.MIN_PRIORITY);	// 1

예제

package chap12;

public class PriorityEx {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for(int i=1;i<=10;i++) {
			Thread thread = new CalcThread("thread"+i);
			if(i==10) {
				thread.setPriority(Thread.MAX_PRIORITY);
			}else {
				thread.setPriority(Thread.MIN_PRIORITY);
			}
			thread.start();
		}
	}

}
package chap12;

public class CalcThread extends Thread{
	public CalcThread(String name) {
		// 스레드 이름 설정 
		setName(name);
	}
	
	@Override
	public void run() {
		for(int i=0;i<2000000000;i++) {
			
		}
		System.out.println(getName());
		
	}
}

동기화 메소드와 동기화 블록

공유 객체를 사용할 때 주의할 점

싱글 스레드 프로그램에서는 한 개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업해야하는 경우가 있다. 이러한 경우에 스레드 A를 사용하던 객체가 스레드 B에 의해서 상태가 변경될 수 있기 때문에 스레드 A가 의도했던 것과 다른 결과를 산출할 수 있다.

package chap12;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Calculator calculator = new Calculator();
		
		User1 user1 = new User1();
		user1.setCalculator(calculator);
		user1.start();
		User2 user2 = new User2();
		user2.setCalculator(calculator);
		user2.start();
	}
}
package chap12;

public class Calculator {
	private int memory;

	public int getMemory() {
		return memory;
	}

	public void setMemory(int memory) {
		this.memory = memory;
		try {
			Thread.sleep(2000);
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println(Thread.currentThread().getName() + ": "+this.memory);
	}
	
}
package chap12;

public class User1 extends Thread{
	private Calculator calculator;

	public void setCalculator(Calculator calculator) {
		this.setName("USER1");
		this.calculator = calculator;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		calculator.setMemory(100);
	}
	
}
package chap12;

public class User2 extends Thread{
	private Calculator calculator;

	public void setCalculator(Calculator calculator) {
		this.setName("USER2");
		this.calculator = calculator;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		calculator.setMemory(50);
	}
	
}
USER1: 50
USER2: 50

User1 스레드가 Calculator 객체의 memory 필드에 100을 저장하고 2초간 멈춘 상태가된다. 그동안에 User2가 memory값을 50으로 변경한다. User1이 2초가 지나고 memory 필드의 값을 출력하면 50이 나오는 것을 볼 수 있다.

동기화 메소드 및 동기화 블록

스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드 작업이 끝날 때까지 객체에 다른 스레드가 접근할 수 없도록 해야한다. 멀티 스레드 프로그램에서 **단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical section)**이라고 한다. 자바에서 임계 영역을 지정하기 위해서 동기화(synchronized) 메소드와 동기화 블록을 제공한다. 스레드가 객체 내부의 동기화 메소드 혹인 블록에 들어가면 즉시 객체에 잠금을 걸어서 다른 스레드가 임계영역코드를 실행하지 못하도록 한다.

public synchronized void method(){
    //임계영역, 단 하나의 스레드만 실행
}

동기화 메소드는 메소드 전체 내용이 임계영역이므로 스레드가 동기화 메소드를 실행하는 즉시 객체에는 잠금이 일어나고, 스레드가 동기화 메소드를 종료하면 잠금이 풀린다.

메소드 전체가 아니라 일부만 임계영역으로 만들 수도 있다.

public void method(){
    //여러스레드가능
    synchronized(공유객체){
        //임계영역
    }
    // 여러 스레드 실행 가능
}

예제

package chap12;

public class Calculator {
	private int memory;

	public int getMemory() {
		return memory;
	}

	public synchronized void setMemory(int memory) {
		this.memory = memory;
		try {
			Thread.sleep(2000);
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println(Thread.currentThread().getName() + ": "+this.memory);
	}
	
}
USER1: 100
USER2: 50

동기화메소드로 바꾼 결과 user1은 100, user2는 50이 나오는 것을 볼 수 있다.

package chap12;

public class Calculator {
	private int memory;

	public int getMemory() {
		return memory;
	}

	public void setMemory(int memory) {
		synchronized (this) {
			this.memory = memory;
			try {
				Thread.sleep(2000);
			} catch (Exception e) {
				// TODO: handle exception
			}
			System.out.println(Thread.currentThread().getName() + ": "+this.memory);
		}	
	}	
}

다음과 같이 동기화 블록으로도 만들 수 있다.

스레드 상태

스레드 객체를 생성(New)하고, start() 메소드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만 사실은 **실행 대기 상태(Runnable)**가된다.

  • 실행대기상태 : 아직 스케줄링이 되지 않아서 실행을 기다리고 있는 상태

실행 대기 상태에 있는 스레드 중에서 스레드 스케줄링으로 선택된 스레드가 비로서 CPU를 점유하고 run()메소드를 실행한다. 이때를 실행상태(Running) 라고한다.

실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 스레드 스케줄링에 의해서 다시 대기 상태로 돌아갈 수 있다. 그리고 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가된다. 이렇게 스레드는 실행 대기 상태와 실행 상태를 번갈아가면서 자신의 run() 메소드를 조금씩 수행한다.

더 이상 실행할 코드가 없으면 스레드의 실행은 멈추게되고 이 상태를 종료상태(Dead, Termianted) 라고한다.

경우에 따라서 스레드는 실행 상태에서 일시 정지 상태로 가기도한다.

  • 일시정지상태 : 스레드가 실행할 수 없는 상태

    • WAITING

    • TIMED_WAITING

    • BLOCKED

스레드가 일시정지상태에서 다시 실행상태로 가기 위해서는 실행대기상태로 가야한다.

getState()

스레드의 상태를 코드에서 확인할 수 있는 메소드이다.

상태
열거상수
설명

객체 생성

NEW

스레드 객체가 생성, 아직 start() 메소드 호출전 상태

실행 대기

RUNNABLE

실행 상태로 언제든지 갈 수 있는 상태

일시정지

WAITING

다른 스레드가 통지할 때까지 기다리는 상태

TIMED_WAITING

주어진 시간동안 기다리는 상태

BLOCKED

사용하고자 하는 객체의 락이 풀릴 때까지 기다리는 상태

종료

TERMINATED

실행을 마친 상태

스레드 상태 제어

실행중인 스레드의 상태를 변경하는 것을 스레드 상태 제어라한다.

상태 제어 메소드

메소드
설명

interrupt()

일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜서 예외 처리 코드(catch)에서 실행 대기 상태 혹은 종료 상태로 갈 수 있도록한다.

notify() notifyAll()

동기화 블록 내에서 wait() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다.

sleep(long millis) sleep(long millis, int nanos)

주어진 시간동안 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다.

join() join(long millis) join(long millis, int nanos)

join() 메소드를 호출한 스레드는 일시 정지 상태가 된다. 다시 대기 상태로 가려면, join()메소드를 멤버로 가지는 스레드가 종료되거나, 매개값으로 주어진 시간이 지나야한다.

wait() wait(long millis) wait(long millis, int nanos)

동기화(synchronized) 블록 내에서 스레드를 일시 정지 상태로 만든다. 매개값으로 주어진 시간이 지나면 자동적으로 실행 대기 상태가 된다. 시간이 주어지지 않으면 notify(), notifyAll() 메소드에 의해서 실행 대기 상태로 갈 수 있다.

yield()

실행 중에 우선순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 된다.

sleep() : 주어진 시간동안 일시 정지

실행 중인 스레드를 일정시간 멈추게 할때 사용하는 Thread 정적 메소드이다.

try{
    Thread.sleep(1000);
}catch(InterruptedException e){
    // interrupt() 메소드가 호출되면 실행
}

매개값에는 얼마 동안 일시 정지 상태로 있을 것인지, 밀리세컨드(1/1000) 단위로 시간을 주면 된다.

일시 정지 상태에서 주어진 시간이 끝나기 전에 interrupt() 메소드가 호출되면 InterruptedException이 발생하기 때문에 예외 처리가 필요하다.

yield() : 다른 스레드에게 실행 양보

스레드가 처리하는 작업은 반복적인 실행을 위해서 for문이나 while 문을 포함하는 경우가 많다.

public void run(){
    while(true){
        if(work){
            System.out.println("Thread1 작업내용");
        }
    }
}

이 코드에서 스레드가 시작되어 run() 메소드를 실행하면 while문은 무한 반복 실행하게 된다. 이것보다는 다른 스레드에게 실행을 양보하는 것이 전체 프로그램 성능이 도움이 된다.

public void run(){
    while(true){
        if(work){
            System.out.println("Thread1 작업내용");
        }else{
            Thread.yield();
        }
    }
}

yield() 메소드를 호출한 스레드는 실행 대기 상태로 돌아가고 동일한 우선순위 또는 높은 우선순위를 갖는 다른 스레드가 실행 기회를 가질 수 있도록해준다.

join() : 다른 스레드의 종료를 기다림

스레드는 다른 스레드와 독립적으로 실행하는 것이 기본이지만 다른 스레드가 종료될 때까지 기다렸다가 실행해야하는 경우가 발생할 수도 있다. 예를 들어서 계산 작업을 하는 스레드가 모든 계산 작업을 마쳤을 때, 계산 결과값을 받아 이용하는 경우이다.

join() 메소드를 호출하면 다른 스레드가 종료될때까지 일시 정지 상태가 되고 종료되면 join()이 풀려서 다음 코드를 수행하게 된다.

package chap12;

public class SumThread extends Thread{
	private long sum;

	public long getSum() {
		return sum;
	}

	public void setSum(long sum) {
		this.sum = sum;
	}
	
	@Override
	public void run() {
		for(int i=1;i<=100;i++)
			sum+=i;
	
	}
}
package chap12;

public class Main {

	public static void main(String[] args) {

		SumThread sumThread = new SumThread();
		sumThread.start();
		
		try {
            // sumThread가 종료될때까지 main thread를 일시 정지 시킨다.
			sumThread.join();
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		System.out.println("합 : "+sumThread.getSum());
	}

}

wait(), notify(), notifyAll() : 스레드 간 협업

경우에 따라서 두 개의 스레드를 교대로 번갈아가며 실행해야 할 경우가 있다. 정확한 교대 작업이 필요한 경우에 자신이 작업이 끝나면 상대방 스레드를 일시 정지 상태에서 풀어주고, 자신은 일시정지 상태로 만드는 것이다.

공유 객체 가 핵심이다. 공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메소드로 구분해 놓고, 한 스레드가 작업을 완료하면 notify() 메소드를 호출해 일시 정지 상태에 있는 다른 스레드를 실행 대기 상태로 만들고, 자신은 두 번 작업하지 않도록 wait() 메소드를 호출해 일시정지 상태로 만든다.

wait(long timeout)이나 wait(long timeout, int nanos) 를 사용하면 notify()를 호출하지 않아도 지정된 시간이 지나면 스레드가 자동적으로 실행 대기 상태가 된다.

notifyAll() 메소드는 wait() 에 의해 일시 정지된 모든 스레드를 실행 대기 상태로 만든다.

주의 할점은 이 메소드들은 동기화 메소드 또는 동기화 블록 내에서만 사용할 수 있다.

package chap12;

public class WorkObj {
	public synchronized void methodA() {
		System.out.println("ThreadA의 methodA 실행 ");
		// 일시정지->실행대기!
		notify();
		
		try {
			//일시정지상태로 만듦!
			wait();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	public synchronized void methodB() {
		System.out.println("ThreadB의 methodB 실행 ");
		// 일시정지->실행대기!
		notify();
		
		try {
			//일시정지상태로 만듦!
			wait();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

}
package chap12;

public class ThreadA extends Thread{
	private WorkObj workObj;
	
	public ThreadA(WorkObj workObj) {
		this.workObj = workObj;
	}
	
	@Override
	public void run() {
		for(int i=0;i<10;i++)
			workObj.methodA();
	}
}
package chap12;

public class ThreadB extends Thread{
	private WorkObj workObj;
	
	public ThreadB(WorkObj workObj) {
		this.workObj = workObj;
	}
	
	@Override
	public void run() {
		for(int i=0;i<10;i++)
			workObj.methodB();
	}
}
package chap12;

public class Main {

	public static void main(String[] args) {
		// 공유객체생성 
		WorkObj sharedObj = new WorkObj();
		
		ThreadA threadA = new ThreadA(sharedObj);
		ThreadB threadB = new ThreadB(sharedObj);
		
		threadA.start();
		threadB.start();
	}

}
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행 
ThreadA의 methodA 실행 
ThreadB의 methodB 실행