프로세스(process)란?
보통 프로세스라고 함은 실행 중인 하나의 프로그램을 말한다.
-하나의 프로그램이 여러 개의 프로세스를 만들기도 한다.(ex. 인터넷 창 등)
멀티 태스킹 (multi tasking)
멀티 테스킹은 통상 두 가지 이상의 작업을 동시에 처리하는 것을 칭한다.
구현 방법
멀티 프로세스
-> 독립적 프로그램을 여러 개 실행 처리
멀티 스레드
-> 하나의 프로그램을 실행하여, 내부적으로 여러 가지 작업을 처리하는 것
메인 스레드(main thresd)
모든 자바 프로그램은 JVM의 메인 스레드가 main()를 실행하면서 시작한다.
메인 스레드와 작업 스레드
메인 스레드는 스레드들을 만들어서 병렬로 코드를 실행할 수 있으며,
그것이 곳, 멀티 태스킹을 수행하는 것이다.
프로세스의 종료
메인 스레드가 작업 스레드보다 먼저 종료되더라도, 작업 스레드가 계속 실행 중이라면,
프로세스는 종료되지 않는다.
작업 스레드 생성과 실행
몇 개의 작업을 병렬로 실행해야 할지 설계 단에서 결정해야 한다.
작업 스레드를 생성하는 방법은 아래와 같다.
Thread 클래스로부터 직접 생성한다.
Thread 하위 클래스로부터 생성한다.
package kr.co.kihd.singlethread;
import java.awt.Toolkit;
public class BeepSoundTest {
//main()은 JVM이 실행한다.
public static void main(String[] args) {
//현재 실행중인 스레드명을 출력함.
System.out.println(Thread.currentThread().getName());
//Toolkit은 추상클래스인데, 그중에 GUI관련된 메서드들로 구성되어진 인스턴스를
//getDefaultToolkit()를 통해서 참조를 얻어냄.
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
toolkit.beep(); //비프음 출력
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i = 0; i<5; i++) {
System.out.println("삐슝~");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//싱글스레드를 멀티스레드로 바꿔서 프로그램을 만드는것이 우리의 목표임.
}
}
package kr.co.kihd.singlethread;
import java.awt.Toolkit;
public class BeepSoundTest {
//main()은 JVM이 실행한다.
public static void main(String[] args) {
//현재 실행중인 스레드명을 출력함.
System.out.println(Thread.currentThread().getName());
//Toolkit은 추상클래스인데, 그중에 GUI관련된 메서드들로 구성되어진 인스턴스를
//getDefaultToolkit()를 통해서 참조를 얻어냄.
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
toolkit.beep(); //비프음 출력
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for(int i = 0; i<5; i++) {
System.out.println("삐슝~");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//싱글스레드를 멀티스레드로 바꿔서 프로그램을 만드는것이 우리의 목표임.
}
}
직접 생성하는 멀티스레드 방법 3가지
방법 1 --> 구현 객체 (Runnable) 대입
방법 2 --> 익명 구현 객체
방법 3 --람다식(함수적 인터페이스) JDK1.8, 코드 절약, 가독성
package kr.co.kihd.multithread1;
import java.awt.Toolkit;
//Runnable인터페이스를 직접 구현하고 있다.
public class Beep implements Runnable{
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
toolkit.beep(); //비프음 출력
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.co.kihd.multithread1;
import java.awt.Toolkit;
public class BeepSoundTest {
//main()은 JVM이 실행함.
public static void main(String[] args) {
//직접 생성하는 멀티스레드 방법 3가지
//방법 1 --> 구현객체 (Runnable) 대입
// Runnable runnable = new Beep(); //구현객체 대입
// Thread thread = new Thread(runnable);
// thread.start();
//방법 2 --> 익명구현객체
// Thread thread = new Thread(new Runnable() {
//
// @Override
// public void run() {
//
// Toolkit toolkit = Toolkit.getDefaultToolkit();
// for(int i = 0; i < 5; i++) {
// System.out.println("for문을 실행하는 스레드 이름 : " +
// Thread.currentThread().getName());
// toolkit.beep(); //비프음 출력
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// }
//
// }
// });
//
// thread.start();
//방법 3 --람다식(함수적 인터페이스) JDK1.8, 코드절약, 가독성
Thread thread = new Thread( ()-> {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
toolkit.beep(); //비프음 출력
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
for(int i = 0; i<5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
System.out.println("삐슝~");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
for문을 실행하는 스레드 이름 : main
삐슝~
for문을 실행하는 스레드 이름 : Thread-0
for문을 실행하는 스레드 이름 : main
삐슝~
for문을 실행하는 스레드 이름 : Thread-0
for문을 실행하는 스레드 이름 : main
삐슝~
for문을 실행하는 스레드 이름 : Thread-0
for문을 실행하는 스레드 이름 : main
삐슝~
for문을 실행하는 스레드 이름 : Thread-0
for문을 실행하는 스레드 이름 : main
삐슝~
for문을 실행하는 스레드 이름 : Thread-0
작업 스레드의 이름
스레드의 이름
메인 스레드 이름 : main -> JVM 생성과 동시에 부여함
작업 스레드 이름 (자동 설정) : Thread-n
동시성과 병렬성
동시성 - 멀티 작업 위해 하나의 코어에서 멀티 스레드가 번갈아 가며 실행하는 성질
병행성 - 멀티 작업을 위해 멀티 코어에서 개별 스레드를 동시에 실행하는 성질
package kr.co.kihd.multithread2;
import java.awt.Toolkit;
//thread 클래스 자체를 상속을 받아서 스레드 클래스로 만듬
public class Beep extends Thread {
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i = 0; i < 5; i++) {
System.out.println("for문을 실행하는 스레드 이름 : " +
Thread.currentThread().getName());
toolkit.beep(); //비프음 출력
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.co.kihd.multithread2;
//Thread를 상속한 Beep클래스를 이용한 구현
public class BeepTest {
public static void main(String[] args) {
Thread thread = new Beep(); //필드의 다형성
thread.start();
for(int i = 0; i<5; i++) {
System.out.println("삐슝~");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
타이머 만들기 예제
package kr.co.kihd.multithread3;
public class TimerThread extends Thread {
@Override
public void run() {
for(int i = 10; i>0; i--) {
System.out.println("남은 시간 : " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.co.kihd.multithread3;
import javax.swing.JOptionPane;
public class TimerTest {
public static void main(String[] args) throws Exception {
Thread thread = new TimerThread();
thread.start();
String input = JOptionPane.showInputDialog("10초 안에 값을 입력하세요.");
if(input != null){
System.out.println("입력 값 : " + input);
System.exit(0);
}else {
Thread.sleep(10000); //10초를 대기한다.
System.out.println("입력하지 못했습니다. 삐쓩!!");
System.exit(0);
}
}
}
남은 시간 : 10
남은 시간 : 9
남은 시간 : 8
남은 시간 : 7
남은 시간 : 6
남은 시간 : 5
남은 시간 : 4
남은 시간 : 3
남은 시간 : 2
남은 시간 : 1
입력하지 못했습니다. 삐쓩!!
스레드의 이름을 얻는 2가지 방법
1번째(정적 메서드 이용)
Thread.currentThread(). getName()
2번째(인스턴스 메서드 이용)
this.getName()
package kr.co.kihd.threadname;
public class ThreadGumi extends Thread{
public ThreadGumi() {
this.setName("ThreadGumi"); //스레드 이름을 지정하고 있다.
}
@Override
public void run() {
System.out.println("[현재 실행 중인 스레드] : " + this.getName());
for(int i = 0; i<5; i++) {
System.out.println(this.getName() + "가 출력한 내용 : " + i);
}
}
}
package kr.co.kihd.threadname;
public class ThreadDaegu extends Thread{
public ThreadDaegu() {
this.setName("ThreadDaegu"); //스레드 이름을 지정하고 있다.
}
@Override
public void run() {
System.out.println("[현재 실행 중인 스레드] : " + this.getName());
for(int i = 0; i<5; i++) {
System.out.println(this.getName() + "가 출력한 내용 : " + i);
}
//스레드의 이름을 얻는 2가지 방법
/*
* 1번째(정적메서드 이용)
* Thread.currentThread().getName()
* 2번째(인스턴스 메서드 이용)
* this.getName()
*/
}
}
스레드 우선순위
스레드 스케줄링
스레드의 개수가 코어의 수보다 많을 경우
스레드를 어떤 순서로 동시성으로 실행할 것인가 경정 -> 스레드 스케줄링
스케줄링에 의해 스레드들은 번갈아 가며 run() 메서드를 조금씩 실행
스레드 우선순위
스레드들이 동시성을 가질 경우 우선적으로 실행할 수 있는 순위
우선순위는 1(낮음)에서부터 10(높음)까지로 부여
=모든 스레드의 우선순위는 5의 우선순위
package kr.co.kihd.priority;
public class CalcThread extends Thread {
long sum;
public CalcThread(String name) {
this.setName(name);
}
@Override
public void run() {
for(int i = 0; i<20000000; i++) {
sum += i;
}
System.out.println("[작업 완료 스레드] : " + this.getName());
System.out.println("[합계] : " + this.sum);
}
}
package kr.co.kihd.priority;
public class PriorityTest {
public static void main(String[] args) {
Thread thread0 = Thread.currentThread();
System.out.println(thread0.getName() + " : " + thread0.getPriority());
for(int i = 1; i <= 5; i++) {
Thread thread = new CalcThread("Thread : " + i);
//Thread1,2,3,4는 우선운위가 가장 낮다.
if(i != 5) {
thread.setPriority(Thread.MIN_PRIORITY);
//thread.setPriority(1); //위와 동일
}
//우선순위가 10으로 가장 높다.
else {
thread.setPriority(Thread.MAX_PRIORITY);
//thread.setPriority(10); //위와 동일
}
thread.start();
}
}
}
동기화 메서드와 동기화 블록
공유 객체를 사용할 때의 주의할 점
멀티 스레드가 하나의 객체를 공유해서 생기는 오류
동기화 메서드 및 동기화 블록
단 하나의 스레드만 실행할 수 있는 메서드 또는 블록을 말한다.
다른 스레드는 메서드나 블록이 실행이 끝날 때까지 대기해야 한다.
동기화가 되는 영역 => 임계 영역
해당 객체를 독점적으로 사용할 수 있는 권한 => 모니터
synchronized
한 스레드만이 독점적으로 실행되어야 하는 부분(동기화 코드)을 표시하는 키워드
임계 영역을 표기하는 키워드
wait()
다른 스레드가 notify()를 불러줄 때까지 기다린다.
notify()
특정한 스레드 하나만 깨워주는 메서드
notifyAll()
대기 중인 거 다 깨워주는 메서드
동기화 예제
package kr.co.kihd.synchronizedd;
//공유객체
public class Calculator {
private int memory;
int value = 100;
public int getMemory() {
return this.memory;
}
//동기화 메서드 => 데이터 신뢰성 보장하기 위해 반드시 동기화처리가 필수다.
public synchronized void setMemory(int memory) {
this.memory = memory;
//1초간 일시정지
//System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +
" : " + this.getMemory());
}
//비동기화 메서드
public synchronized void print() {
System.out.println(Thread.currentThread().getName() +
" " + "value값 : " + this.value);
for(int i = 0; i<10; i++) {
System.out.println(Thread.currentThread().getName()
+" " + i);
}
}
}
package kr.co.kihd.synchronizedd;
public class UserA extends Thread {
//공유객체
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.calculator = calculator;
this.setName("UserA"); //스레드 이름 지정
}
@Override
public void run() {
this.calculator.print();
//공유객체의 필드인 memory값에 100을 변경
this.calculator.setMemory(100);
}
}
package kr.co.kihd.synchronizedd;
public class UserB extends Thread {
//공유객체
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.calculator = calculator;
this.setName("UserB"); //스레드 이름 지정
}
@Override
public void run() {
this.calculator.print();
//공유객체의 필드인 memory값에 50을 변경 //서로다른 메모리값 주기.
this.calculator.setMemory(50);
}
}
package kr.co.kihd.synchronizedd;
public class NnsynchronizedTest {
public static void main(String[] args) {
//공유객체 생성
Calculator calculator = new Calculator();
UserA userA = new UserA();
userA.setCalculator(calculator);
UserB userB = new UserB();
userB.setCalculator(calculator);
userA.start();
userB.start();
}
}
화장실 예제 동기화
package kr.co.kihd.synchronizedd2;
public class Toilet {
//플레그 변수
private boolean seat;
public synchronized void use() throws Exception {
String name = Thread.currentThread().getName();
if(this.seat == false) {
System.out.println(name + "가/이 화장실에 침입했습니다.");
this.seat = true;
Thread.sleep(1000);
System.out.println(name + " 가/이 말 : 아 시원하다!");
this.seat = false;
System.out.println(name + " 가/이 화장실 사용을 마쳤습니다.");
System.out.println();
}
else {
System.out.println(name + " 가/이 화장실 사용중입니다. 기다려주세요!");
}
}
public synchronized void noke() {
System.out.println(Thread.currentThread().getName() +
" 똑똑!");
}
}
package kr.co.kihd.synchronizedd2;
public class UsingToilet extends Thread {
//공유객체
private Toilet toilet;
public UsingToilet(Toilet toilet, String name) {
this.toilet = toilet;
this.setName(name);
}
@Override
public void run() {
try {
this.toilet.noke();
this.toilet.use();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package kr.co.kihd.synchronizedd2;
public class ToiletTest {
public static void main(String[] args) {
Toilet toilet = new Toilet();
Thread thread1 = new UsingToilet(toilet, "아버지");
Thread thread2 = new UsingToilet(toilet, "어머니");
Thread thread3 = new UsingToilet(toilet, "아들");
Thread thread4 = new UsingToilet(toilet, "딸");
thread1.start(); thread2.start();
thread3.start(); thread4.start();
}
}
스레드 6가지 상태값 확인
package kr.co.kihd.state;
//스레드의 상태를 알아보기 위한 클래스
public class TargetThread extends Thread{
@Override
public void run() {
//20억번 루핑, 실행 -> 실행대기(Runnable) -> 실행
for(long i=0; i<2000000000; i++) {
}
//일시정지(TIMED_WAITING)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//20억번 루핑, 실행 -> 실행대기(Runnable) -> 실행
for(long i=0; i<2000000000; i++) {
}
} //종료(TERMINATED)
}
package kr.co.kihd.state;
public class StatePrintThread extends Thread{
//스레드의 상태를 알아보기 위한 멤버로 선언
//참조번지를 갖는 객체 추가
private TargetThread targetThread;
public StatePrintThread(TargetThread targetThread) {
this.targetThread = targetThread;
}
@Override
public void run() {
while(true) {
//스레드의 상태정보를 알아낸다.
//Thread.state는 Thread클래스 Enum(열거체)타입이다.
Thread.State state = targetThread.getState();
System.out.println("타겟스레드의 상태 : " + state);
//스레드가 생성되었다면 실행
if(state == Thread.State.NEW) {
targetThread.start();
}
//무한루프를 빠져 나가는 코드(스레드 종료되었느냐?)
if(state == Thread.State.TERMINATED) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package kr.co.kihd.state;
public class ThreadStateTest {
public static void main(String[] args) {
//스레드의 라이프사이클을 알아보는 내용
Thread thread = new StatePrintThread(new TargetThread());
thread.start();
}
}
타겟스레드의 상태 : NEW
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : TIMED_WAITING
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : RUNNABLE
타겟스레드의 상태 : TERMINATED