와!!! 이제 고급편 들어간다 (12, 13, 14챕터)
꼼꼼히 차근차근 공부해보자 챕터 12는 스레드
이전 챕터에서 스레드라는 단어가 나온 적이 있었다.
아래 내용!!
중첩 클래스 ㅡ 로컬 클래스에서 언급이 됐었다.
비동기 처리를 위해 스레드 객체를 만든다?? 12강을 열심히 공부하면 무슨 말인지 알게 되게찌
키워드: 프로세스, 멀티 스레드, 메인 스레드, 작업 스레드, 동기화 메소드
프로세스 : 애플리케이션 실행 -> 운영체제로부터 실행에 필요한 메모리 할당받아 실행됨 : process
ㄴ실행 중인 하나의 애플리케이션을 말한다.
ㄴ메모장 애플리케이션을 2개 실행 => 메모장 프로세스 2개가 생성된 것 (멀티 프로세스)
스레드 : 프로세스 내부에서 코드의 실행 흐름 : thread
ㄴ애플리케이션 개발의 필수 요소
<<멀티 스레드는 예외 처리가 중요하다>>
ㅇ멀티 프로세스 : 각 프로세스가 '자신의 메모리'를 할당받아 쓰므로 각 프로세스는 독립적이다.
ㄴ워드 엑셀 동시 실행 중 엑셀 프로세스가 먹통이어도 워드 프로세스에는 지장 없다.
ㅇ멀티 스레드 : 하나의 프로세스의 내부에 생성된다. ->하나의 스레드가 예외를 일으키면 프로세스 자체가 종료될 수 있다.
ㄴ만약 메신저 프로세스의 파일전송 스레드에서 예외발생시 메신저 프로세스가 종료되며, 이에 따라 당연히 다른 스레드(채팅)도 종료된다.
ㄴ그래서 멀티 스레드는 예외 처리가 베리베리 중요하다
(접은글) 멀티 스레드 이용 예
멀티 스레드 이용 예
ㅇ 데이터를 분할해 병렬로 처리 <- 대용량 데이터 처리시간 단축
ㅇ UI를 가지고 있는 애플리케이션에서 네트워크 통신을 하기 위해 사용
ㅇ 다수 클라이언트의 요청을 처리하는 서버 개발시 필요
그렇다고 한다!
학습할 내용
ㅇ메인 스레드
ㅇ작업 스레드 생성과 실행
ㄴThread 클래스로부터 직접 생성
ㄴThread 하위클래스로부터 생성
ㄴ스레드의 이름
ㅇ동기화 메소드
ㄴ공유 객체를 사용할 때의 주의할 점
ㄴ동기화 메소드
메인 스레드
앗 지난번에
모든 프로그램은 main()메소드에서 가장 먼저 시작된다고 배웠었다 (entry point)
그런데 여기서 다시 이렇게 설명해준다
"자바의 모든 애플리케이션은 메인스레드가 main()메소드를 실행하면서 시작"
ㄴ메인스레드는 main()의 첨부터 아래로 쭉쭉 실행하다가 'main()메소드 끝까지가거나, return문 만나면' 실행이 종료된다. (코드의 실행 흐름 = 스레드!!)
메인 스레드는
필요에 따라 작업스레드를 여러개 만들어서 병렬로 코드를 실행할 수 있다
ㄴ멀티스레드를 생성 (멀티태스킹 수행)
(싱글 스레드 애플리케이션에서는 '메인스레드'가 종료되면 프로세스가 종료된다.
멀티 스레드 애플리케이션에서는 실행중인 스레드가 있다면 프로세스는 종료되지 않는다. 메인스레드가 작업스레드보다 먼저 종료되어도 상관 없는 얘기)
작업 스레드 생성과 실행
ㅁ멀티 스레드로 실행하는 애플리케이션 개발을 위해 결정할 것
ㄴ 몇 개의 작업을 병렬로 실행할 것인가?
ㄴ 각 작업별로 어떤 스레드를 생성할 것인가?
작업 스레드도 객체로 생성된다. -> 작업 스레드의 클래스가 필요하다!
<<클래스 생성>>
ㅇjava.lang.Thread 클래스를 직접 객체화하여 생성하기
ㅇjava.lang.Thread 클래스를 상속하기 => 하위 클래스를 만들어 객체 생성하기
1) Thread 클래스로부터 객체를 직접 생성
ㅇRunnable을 매개값으로 하는 생성자를 호출한다.
Thread thread = new Thread(Runnable target);
ㄴRunnable : 작업스레드가 실행할 수 있는 코드를 가지고 있는 객체 + 인터페이스 타입
ㄴ즉, Runnable을 구현하는 객체를 만들어 대입해야 한다.
ㄴ구현 클래스는 run()을 재정의해서 작업스레드가 실행할 코드를 작성해야 한다.
★작업 스레드의 생성★
ㅇRunnable인터페이스 구현 클래스 작성하기 + Runnable 구현 객체 생성
ㅇ구현 클래스를 매개값으로 해서 Thread 생성자 호출하기
★구현객체 넣어 호출까지 해야 비로소 작업 스레드가 생성된다.
Runnable task = new Task(); //Task는 Runnable구현 클래스지
Thread thread = new Thread(task);
For 코드절약, Runnable 익명구현객체를 매개값으로 넣는다!!
Thread 생성자 호출할 때 매개값에 익명구현객체를 넣는 방법이 더 많이 사용된다.
작업 스레드를 생성했는데 실행은 어떻게 할까?
start() 메소드를 호출함으로써!!
thread.start();
start()메소드 실행 ->
매개값의 Runnable 구현객체의 run()메소드 실행하며 해당 작업스레드에게 맡겨진 작업 처리 (병렬)
작성하면서 익혀보자
<비프음 발생과 출력>
ㅇ메인스레드만 이용하기 ㅡ 동시진행 불가, 순차적 진행
ㅇ메인스레드, 작업스레드 이용하기 ㅡ 동시 진행 가능
ㅇ메인스레드만 이용하여 비프음 발생, 출력 기능 작성
ㅡ>비프음 5번 다 울린 다음에서야 "띵"문자열이 5번 출력된다.
ㅡ>비프음 발생과 동시에 문자열 출력은 불가능하다.
+java.awt.ToolKit을 만나봤다. 비프음은 Toolkit 객체의 메소드로 이용 가능하구나
+Thread 클래스에 sleep()이라는 메소드도 만났다. 일시정지를 이렇게 하는구나
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
ㅇ메인스레드,작업스레드 동시 이용하기
ㅡ>"띵"문자열 출력과 동시에 비프음 발생 가능
ㅡ>출력 스레드와 비프음 스레드를 각각 작성해야 한다. (작업 스레드 정의)
작업을 정의하는 Runnable 구현클래스를 작업스레드로 작성해보자.
Thread thread = new Thread(Runnable target);
java.lang.Thread클래스로부터 작업 스레드 객체를 직접 생성하려면 Runnable을 매개값으로 갖는 생성자를 호출해야 했었다. Runnable은 인터페이스 타입으로, 구현 객체를 만들어 대입해야 했다. Runnable에는 run()메소드 하나가 정의되어 있었고, run()을 재정의해야 했다. ->구현클래스에서 작업스레드 코드를 작성해야 했다. 익명 구현 객체로 정의를 많이 한다고 했다. 이렇게 작성된 작업 스레드는 thread명.start();로 호출된다.
작업스레드 클래스, 객체 생성하기
2) Thread 클래스를 상속하여 : 하위 클래스로부터 객체 생성
작업스레드를 Runnable로 만들지 않는 방법
Thread의 하위클래스로 작업 스레드를 정의할 수 있다.
ㅇThread클래스를 상속하고 run()메소드를 재정의한다!!
public class WorkerThread extends Thread { @Override public void run() { 스레드가 실행할 코드; } } Thread thread = new WorkerThread(); |
<익명자식객체 생성을 통해> Thread thread = new Thread() { public void run() { 스레드가 실행할 코드; } }; |
ㄴ그리고 작업스레드의 실행은 마찬가지로 thread명.start();
<<비프음 예제>>
이번에는 Runnable을 생성하지 않고
Thread의 하위 클래스에 작업스레드를 정의해보자.
+스레드의 이름
메인 스레드 이름: main
직접 생성한 스레드: 자동적으로 Thread-n (스레드 번호 부여)
ㄴ스레드 이름을 직접 지정하고 싶다면 : setName()메소드를 통해 =>thread.setName("스레드이름");
ㄴ스레드 이름을 알고 싶다면 : thread.getName();
★setName(), getName()은 Thread클래스의 인스턴스 메소드이다
즉, 객체생성이 필요하다. (스레드 객체 참조가 필요)
(아니면 현재 스레드의 참조를 얻을 수도 있다 : Thread thread = Thread.currentThread();)
<<예제: 스레드 이름 설정과 읽기>>
동기화 메소드
ㅇ공유객체를 사용할 때 주의할 점
ㄴ멀티 스레드 프로그램에서는 스레드들이 객체를 공유해서 작업하는 경우가 있으므로, 이 때 주의할 점을 공부하자.
ㅇ동기화 메소드
공유 객체를 사용할 때의 주의할 점
ㄴ객체를 공유하기 때문에 다음과 같은 문제점이 생길 수도 있다
->스레드A가 사용하던 객체를 스레드B가 변경할 수 있음..
★이거 보면... 작업스레드에서 User1의 run()에서는 calculator.setMemory(100) 했지만
메인스레드에서 user1.start(); 실행 결과 memory필드 값이 50으로 설정된 것을 볼 수 있다.
가만보면 User1에서 먼저 user1.start()를 실행하여 memory필드값에 100을 저장한다.
그리고 나서 2초동안 멈추는데,
그 일시정지된 2초 내로 user2.start()가 실행되어 memory필드값에 50을 저장한다.
결과적으로 memory에 50이 저장된 상태 이후에 공유객체의 메소드를 통해 memory필드값을 출력하기 때문에 의도와 다르게 동일한 값이 출력된다.
그래서 공유객체를 사용할 때 조심해야 한다는 것!!
해결 방법은 무엇일까요로리~~
동기화 메소드
ㄴ스레드가 사용중인 객체를 다른 스레드가 변경할 수 없도록
스레드 작업이 끝날 때까지 객체에 잠금을 건다
다른 스레드가 사용할 수 없게 해야한다!!
☆멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역 : 임계 영역 (critical section)
☆자바는 이 임계 영역을 지정하기 위해서 '동기화 메소드'를 제공 (synchronized method)
ㄴ어떤 스레드가 해당 객체 내부의 동기화 메소드를 실행하면 '즉시 객체에 잠금을 건다'
ㄴ=>다른 스레드는 동기화 메소드를 실행하지 못하게 된다.
ㄴ한 객체 내에 동기화 메소드가 여러개 있을 경우에도, 어떤 스레드가 하나의 동기화 메소드를 이용하고 있다면 나머지 동기화 메소드는 실행불가하다. (동기화 메소드가 아닌 일반메소드는 실행 가능)
☆동기화 메소드를 만드는 법 - synchronized 키워드 이용
public synchronized void method() {
임계 영역; //단 하나의 스레드만 실행한다.
}
방금 전에 공유객체사용시 주의할 점을 공부하고 왔는데
동기화메소드를 사용하여 문제점을 피해보자
<<동기화메소드로 수정된 공유 객체 예제>>
동기화 메소드를 이용하는 공유객체를 사용하도록 했다.
>>새로 작성한 sec01.exam08.Calculator클래스로부터의 객체를 쓰도록 수정해주었다.
>>user1스레드가 해당 동기화메소드를 모두 실행하고 마칠 때까지 user2스레드가 동기화메소드를 실행하지 못한다.
그래서 user1스레드가 공유객체의 그 동기화메소드를 통해 memory필드값에 값을 저장하고, 출력을 할 때까지 user2가 필드값을 변경하지 못하게 작성한 것이다.
확인문제
02. ㅠㅠ 2번에 1번 틀렸다.
인터페이스 구현객체를 매개값으로 넣을 때 난 그냥 MusicRunnable로 썼는데..
인터페이스 구현객체를 생성도 안한 상태니까 그냥 저렇게 쓰면 안돼!!ㅠㅠ
new MusicRunnable()이라고 작성했어야 했다.
객체 생성도 안하고 뭘 넣는거야!! ㅠㅅㅠ
'JAVA' 카테고리의 다른 글
[혼자 공부하는 자바] 7/31 13-1 [컬렉션 프레임워크] (0) | 2020.07.31 |
---|---|
[혼자 공부하는 자바] 7/30 12-2 [스레드 제어] [스레드 일시정지] [sleep()] [안전한 종료] [interrupt()] [InterruptedException] [주스레드] [데몬스레드] (0) | 2020.07.31 |
[혼자 공부하는 자바] 7/28 11-2 [java.util 패키지] (0) | 2020.07.29 |
HashMap이 뭘까\('o')/?? (0) | 2020.07.26 |
[JAVA] java.lang 패키지의 Math 클래스 (0) | 2020.07.26 |