일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- ineer join
- 동일성과 동등성
- stringbuilder의 reverse()
- 모던자바
- 스프링환경설정
- 최소공배수
- git 컨벤션
- 래퍼타입
- 유클리드호제법
- while과 two-pointer
- sql 데이터형 변환
- replaceAll()
- 자바 스트링
- 스프링뼈대
- 자바 최소공배수
- 프로그래머스 레벨1
- Git사용법
- string
- toLowerCase()
- 최대공약수
- 최대공약수와 최소공배수
- 스프링
- StringBuilder
- islowercase()
- isuppercase()
- 베주계수
- 자바 유클리드
- string과 stringbuilder
- 자바 최대공약수
- addDoc
- Today
- Total
주노 님의 블로그
운영체제 - 프로세스, 쓰레드, 멀티쓰레딩, 자바에서의 사용 본문
목차
1. 프로그램을 실행할때의 과정
2. 프로세스란
3. 스레드란?
4. 프로세스와 스레드의 차이점은 무엇일까?
5. 멀티스레드란?
6. 멀티스레드를 사용할때의 이점
7. 멀티스레드를 사용할때의 단점
8. 자바에서 쓰레드를 사용하는 방법
9. 데몬쓰레드
10. 쓰레드의 우선순위
11. 쓰레드의 상태
12. 실행상태에서 대기상태로 옮기는 메서드
13. sleep()
14. join()
15. wait()
16. notify()
17. interrupt()
18. yeild()
19. 스레드 메서드의 요약
프로그램을 실행할때는 아래와 같은 과정을 거친다
프로그램 클릭 : 사용자가 실행하려는 프로그램을 선택한다
자원 할당 : 프로그램이 실행되기위해 필요한 자원을 운영체제로부터 할당받는다 : 프로세스 할당
프로그램 실행 : 할당받은 자원을 사용하여 프로세스가 가동되어 프로그램이 실재로 실행된다
프로그램 종료 : 프로그램을 사용자가 종료하면 운영체제로부터 할당받았던 자원을 반환한다.
프로세스란?
운영체제로부터 자원을 할당 받는 작업의 단위이다
스레드란?
스레드는 프로세스 내의 작은 작업 단위이다. 하나의 프로세스를 여러 개의 작은 작업 단위로 나누어 작업을 수행한다. 스레드는 같은 프로세스 내에서 자원을 공유하며 작업을 수행하기 때문에 경량 프로세스라고도 불린다.
운영체제가 프로그램 실행을 위한 프로세스를 할당해줄때 프로세스 안에 프로그램 코드와 데이터 그리고 메모리영역(스택, 힙)을 할당해준다
1. 코드 : 자바 같은 코드
2. 데이터 : 프로그램이 실행중 저장 할 수 있는 저장공간 (전역변수, static변수, 배열등)
3. 메모리영역
stack : 지역변수 매개변수등 리턴 변수를 저장
heap : 동적으로 필요한 변수를 저장하는 공간 (객체)
프로세스가 작업중인 프로그램에서 실행 요청이 들어오면, 쓰레드가 명령을 처리하도록 한다
쓰레드는 메모리공간을 공유를 받아 작업을 수행한다
자바에서는 메모리공간은 jvm내의 영역이다
프로세스와 스레드의 차이점은 무엇일까?
로세스는 코드, 데이터, 스택, 힙을 각각 생성하여 독립적으로 작동한다면.
스레드는 독립적이지 않으며 하나의 프로그램을 여러 개의 스레드가 공유한다. 그러나 스레드는 자체 스택 영역을 가지고 있습니다.
멀티스레드란?
멀티스레드는 병렬 처리를 통해 프로세스 내 작업을 여러 개의 스레드로 처리하는 기법이다.
예를 들어, 어느 웹 사이트의 경우 새 탭을 만들 때마다 하나의 스레드를 만들어 시간이 오래 걸릴 수 있는 반면.
MS 워드는 여러 개의 스레드를 사용하여 하나의 스레드는 텍스트를 포맷하고, 다른 스레드는 입력을 처리하여 더 빠르게 작동할 수 있다
멀티스레드를 사용할때의 이점
1. 스레드의 생성속도가 빠르고, 쉽게 종료될 수 있다
2. 스레드간의 통신이 더 빠르다
싱글 쓰레드는 하나의 쓰레드가 모든 처리를 하는것을 볼 수 있지만
멀티쓰레드는 여러 개의 스레드가 코드, 데이터, 파일을 공유하며 작업을 수행한다.
단점
자원을 공유하면서 동기화 문제가 생길 수 있다
데드락(둘 이상의 쓰레드가 서로의 자원을 원할때 서로의 작업이 종료되기만을 기다려 작업을 진행하지 못하는 상태)이
일어날 수 있다.
자바에서의 쓰레드를 사용하는 방법
public class W01Main {
//psvm으로 메인 쓰레드를 동작시킴
public static void main(String[] args) {
W01 thread = new W01();
thread.start();// 메인 쓰레드에서 새로운 스레드를 시작함
}
}
package week5;
//1. 쓰레드 클래스를 이용하는것
public class W01 extends Thread{
@Override
public void run() {
// 실제 우리가 쓰레드에서 수행할 작업
for (int i = 0; i < 100; i++) {
System.out.print("*");
}
}
}
Thread 클래스를 상속받으면 run() 메서드를 오버라이드하여 쓰레드에서 수행할 작업을 정의한다.
start() 메서드를 호출하여 쓰레드를 실행한다.
Runnable task = () -> {
int sum = 0;
for (int i = 0; i < 50; i++) {
sum += i;
System.out.println(sum);
}
System.out.println(Thread.currentThread().getName() + " 최종 합 : " + sum);
};
Thread thread1 = new Thread(task);
thread1.setName("thread1");//쓰레드 이름을 정함
Thread thread2 = new Thread(task);
thread2.setName("thread2");
//메인 쓰레드 안에서 두개의 추가의 쓰레드가 있음
thread1.start();
thread2.start();
같은 task를 받아서 사용하는 쓰레드들이고, 병렬적으로 실행한다.
쉽게 예를들어 1~50까지 수를 증가하며 더하는 task인데
쓰레드 1과 쓰레드2가 병렬적으로 출력하고 있어서 출력 결과가 섞여서 나타 날 수 있다.
정해진 패턴을 가진것은 아니며, 걸리는 시간이나 동작을 예측할 수 없다
데몬쓰레드
데몬 쓰레드는 사용자가 생성한 쓰레드가 아닌, 낮은 우선순위를 가진 백그라운드 작업을 수행하는 쓰레드이다.
예를 들어, 자바의 가비지 컬렉터가 데몬 쓰레드이다.
setDemon(true) 데몬 스레드로 할당할 수 있다.
public class DaemonThreadExample {
public static void main(String[] args) {
Thread daemonThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("Daemon thread is running...");
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 데몬 스레드로 설정
daemonThread.setDaemon(true);
daemonThread.start();
// 메인 스레드 실행
try {
Thread.sleep(2000); // 메인 스레드를 2초 동안 슬립
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread is finished.");
}
}
위 코드는 메인 스레드를 2초동안 슬립한 후, 종료하는 코드이다
데몬스레드는 2초동안 돌아가게되고 메인스레드가 2초후 종료되는 방식이다
데몬스레드는 백그라운드에서 돌게된다.
원래대로라면 메인스레드는 종료되기까지 기다려야겠지만
데몬 쓰레드는 모든 사용자 스레드가 종료되면 자동으로 종료된다.
쓰레드의 우선순위를 지정 할 수 있다
1~10 사이의 숫자로 숫자가 높을수록 우선순위가 높다
우선순위가 높은 쓰레드는 더 자주 실행될 가능성이 높다
public static void main(String[] args) {
Runnable task1 = () -> {
for (int i = 0; i < 100; i++) {
System.out.print("$");
}
};
Runnable task2 = () -> {
for (int i = 0; i < 100; i++) {
System.out.print("*");
}
};
Thread thread1 = new Thread(task1);
//setPriority로 우선순위를 지정 할 수 있다 10에 가까울수록 우선순위 높음
thread1.setPriority(8);
int threadPriority = thread1.getPriority();
System.out.println("threadPriority = " + threadPriority);
Thread thread2 = new Thread(task2);
//1에 가까울수록 우선순위가 높다
thread2.setPriority(2);
thread1.start();
thread2.start();
}
상대적으로 $는 일찍 끝났다.
컴퓨팅 성능이 좋은 현대에는 그닥 차이가 나지 않을수도 있다 아래의 결과처럼 말이다.
쓰레드 그룹모든 쓰레드는 반드시 하나의 그룹에 속해있어야한다
JVM이 시작되면 system그룹이 생성되고, 기본적으로 system그룹속에 속하게 된다.
메인쓰레드는 system그룹 하위에 main그룹에 포함된다.
public static void main(String[] args) {
Runnable task = () -> {
//쓰레드가 인터럽트를 받지 않는동안 1초동안 쓰레드이름을 찍는다.
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
break;
}
}
System.out.println(Thread.currentThread().getName() + " Interrupted");
};
// ThreadGroup 클래스로 객체를 만듭니다.
ThreadGroup group1 = new ThreadGroup("Group1");
// Thread 객체 생성시 첫번째 매개변수로 넣어줍니다.
// Thread(ThreadGroup group, Runnable target, String name)
// 쓰레드 1과 2는 그룹 1에 포함된다.
Thread thread1 = new Thread(group1, task, "Thread 1");
Thread thread2 = new Thread(group1, task, "Thread 2");
// Thread에 ThreadGroup 이 할당된것을 확인할 수 있습니다.
//그룹이름 찍기
System.out.println("Group of thread1 : " + thread1.getThreadGroup().getName());
System.out.println("Group of thread2 : " + thread2.getThreadGroup().getName());
thread1.start();
thread2.start();
// 현재 쓰레드를 지정된 시간동안 멈추게 합니다.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// interrupt()는 일시정지 상태인 쓰레드를 실행대기 상태로 만듭니다.
//그룹 자체를 인터럽트 시켜버린다.
group1.interrupt();
}
위 코드를 확인하면 쓰레드1과 쓰레드2를 새로운 그룹1로 할당한다
그리고 try catch문에서 메인쓰레드를 5초동안 정지하는 것을 볼 수 있다
맨위의 while문에서 현재 쓰레드가 인터럽트를 발생하지 않는다면.
1초마다 프린트를 찍게한다
1초마다 쓰레드는 번갈아가며 출력하다 5초가 지나면 메인쓰레드의 동작이 돌아가며
마지막의 그룹 1로 설정된 쓰레드를 정지시킨다.
쓰레드 그룹화의 중요성은 원하는 쓰레드를 일괄 처리해야할때 좋다.
쓰레드 상태
실행, 실행대기중인 쓰레드는
실행 또는 실행 대기 중인 쓰레드는 `sleep`, `join`, `wait` 메서드로 일시 정지 상태가 될 수 있으며.
일시 정지 상태에서는 `interrupt`, `notify` 메서드로 다시 실행 대기 상태로 전환될 수 있다.
쓰레드는 new 키워드로 생성된다.
strat() 메서드를 호출하는 순간 쓰레드는 실행 대기 상태가 된다
os의 스케쥴러에 의해 run()메서드를 실행 > 실행대기를 반복한 후, 종료가된다
쓰레드의 상태
객체생성 NEW : 쓰레드 객체 생성, 아직 start() 메서드 호출 전의 상태
실행대기 RUNNABLE : 실행 상태로 언제든지 갈 수 있는 상태
일시정지 WAITING : 다른 쓰레드가 통지(notify) 할 때까지 기다리는 상태
일시정지 TIMED_WAITING : 주어진 시간 동안 기다리는 상태
일시정지 BLOCKED : 사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태
종료 TERMINATED : 쓰레드의 작업이 종료된 상태
실행 상태에서 대기상태로 옮기는 메서드
sleep
현재 쓰레드를 지정된 시간동안만 멈출 수 있다.
public static void sleep( @Range(from = 0, to = Long. MAX_VALUE) long millis )
throws InterruptedException
sleep은 InterruptedException 을 던져주기때문에 try catch문으로 감싸줘야한다.
package thread;
public class SleepThreadExample extends Thread {
private String threadName;
public SleepThreadExample(String name) {
this.threadName = name;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " - Iteration: " + i);
try {
// 스레드를 1초 동안 일시 중지
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(threadName + " was interrupted.");
}
}
System.out.println(threadName + " has finished executing.");
}
public static void main(String[] args) {
SleepThreadExample thread1 = new SleepThreadExample("Thread-1");
SleepThreadExample thread2 = new SleepThreadExample("Thread-2");
// 스레드 시작
thread1.start();
thread2.start();
}
}
sleep은 static이기때문에 클래스타입으로 직접 호출을 해줘야 한다.
sleep은 interrupedException을 발생 시킬 수 있다 따라서 try catch문으로 감싸야한다.
각 스레드는 1초마다 한번씩 멈췄다가 print문을 출력한다.
sleep 메서드는 static 이므로 인스턴스를 통한 호출이든, 클래스를 통한 호출이든 현재 실행중인 쓰레드만 멈추게된다
join()
정해진 시간 동안 지정한 쓰레드가 작업하는것을 기다린다
시간을 지정하지 않았을때는 지정한 쓰레드의 작업이 끝날때까지 기다리게된다.
public class JoinExample {
// 내부 클래스 MyThread 정의
static class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
this.threadName = name;
}
public void run() {
try {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " - " + i);
Thread.sleep(1000); // 1초 대기
}
} catch (InterruptedException e) {
System.out.println(threadName + " interrupted");
}
}
}
public static void main(String[] args) {
// 스레드 생성
MyThread t1 = new MyThread("Thread 1");
MyThread t2 = new MyThread("Thread 2");
MyThread t3 = new MyThread("Thread 3");
try {
t1.start();
t1.join(); // t1 스레드가 완료될 때까지 기다림
t2.start();
t2.join(); // t2 스레드가 완료될 때까지 기다림
t3.start();
t3.join(); // t3 스레드가 완료될 때까지 기다림
} catch (InterruptedException e) {
System.out.println("Main thread interrupted");
}
System.out.println("All threads are finished");
}
}
위 코드는 t1이 task를 실행하면 t2 t3는 join 메서드를 사용하여 대기한다.
결과적으로 t1이 작업을 마치면 t2가 시작되고 t2가 끝나면 t3가 시작된다.
결과는 위와같이 다른 쓰레드는 기다리고 있다
t2.start();
t2.join(1000); // t2 스레드가 완료될 때까지 기다림
t3.start();
t3.join(1000); // t3 스레드가 완료될 때까지 기다림
} catch (InterruptedException e) {
System.out.println("Main thread interrupted");
}
System.out.println("All threads are finished");
시간을 정해줬다면 위와같이 쓰레드가 진행되고, 1초가 지나면 메인쓰레드도 진행되는것을 볼 수 있다.
wait
일정 시간동안 또는 호출되기전까지 스레드를 대기 상태로 만든다. 기다린다
package thread;
public class SimpleWaitExample {
public static void main(String[] args) {
SimpleWaitExample example = new SimpleWaitExample();
// 대기 스레드
Thread waitingThread = new Thread(() -> {
synchronized (example) {
try {
System.out.println("Waiting thread is going to wait.");
example.wait(3000); // 3초 동안 대기
System.out.println("Waiting thread has finished waiting.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Waiting thread was interrupted.");
}
}
});
waitingThread.start();
}
}
지금의 코드는 3초동안 대기후 출력을한다
wait메서드는 synchronized 블록 안에서 호출이되어야하며
지정된 시간동안 대기하거나
notify를 통해 호출되기까지 대기상태를 유지한다.
대기 상태에서 실행대기상태로 옮기는 메서드
notify()
대기중인 쓰레드를 깨운다
package thread;
public class SimpleWaitNotifyExample {
public static void main(String[] args) {
final Object lock = new Object();
// 대기 스레드
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Waiting thread is going to wait.");
lock.wait(); // 대기
System.out.println("Waiting thread has been notified and is running again.");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Waiting thread was interrupted.");
}
}
});
// 알림 스레드
Thread notifyingThread = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000); // 2초 대기
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Notifying thread is going to notify.");
lock.notify(); // 대기 중인 스레드를 깨움
}
});
waitingThread.start();
notifyingThread.start();
}
}
waiting쓰레드는 무한정 대기를한다.
알림쓰레드는 2초간 정지한후 lock객체를 깨운다
interrupt
일시정지 상태인 쓰레드를 실행대기 상태로 만든다
public static void main(String[] args) {
Runnable task = () -> {
//인터럽트가 받지않은동안
while (!Thread.currentThread().isInterrupted()) {
try {
//1초동안 슬립 >> 슬립은 꼭 예외처리!
Thread.sleep(1000);
//1초 슬립후 이름출력하기
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
break;
}
}
System.out.println("task : " + Thread.currentThread().getName());
};
//task를 수행하는 쓰레드를 만들고
Thread thread = new Thread(task, "Thread"); //new
thread.start(); //new >runnable
//start하는순간 task로.
//메인쓰레드는 아래로
//쓰레드가 1초동안 멈춰야겠지만, 메인쓰레드가 인터럽트를 호출시켜서 에러가 뜨게된다.
//따라서 sleep밑에있는 print는 출력되지 않는다!
thread.interrupt();
System.out.println("thread.isInterrupted() = " + thread.isInterrupted());
}
task를 수행하는 쓰레드가 있다.
메인 쓰레드가 쓰레드를 선언하고 시작하면 그 다른 쓰레드는 task를 순회하게 된다
하지만 메인쓰레드는 인터럽트를 바로 걸어버림으로써 1초동안 슬립해야하는 다른 쓰레드는 정지되게 되고,
catch문으로 넘어가며, break가되어 while문을 벗어나게된다.
즉 try문에 있는 print문은 실행되지 않는다.
yeild
yield는 다른 쓰레드에 양보하고 실행 대기 상태로 전환된다.
public static void main(String[] args) {
Runnable task = () -> {
try {
for (int i = 0; i < 10; i++) {
//1초동안 PRINT를 총10개 출력한다.
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
}
} catch (InterruptedException e) {
//쓰레드1은 인터럽트가 발생해 catch문으로 들어오게 된다
//쓰레드 2에게 모든 리소스를 건네주고 실행대기로 간다.
Thread.yield();
}
};
//태스크는 같은 멀티쓰레드
Thread thread1 = new Thread(task, "thread1");
Thread thread2 = new Thread(task, "thread2");
thread1.start();
thread2.start();
try {
//메인쓰레드는 5초를 기다린다
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//쓰레드 1을 멈추게한다
thread1.interrupt();
쓰레드 1 2 선언 > 메인쓰레드는 5초를 기다린후 쓰레드1을 멈추게한다
쓰레드1은 catch문으로 들어가며, yield메서드로인해 쓰레드2에게 리소스를 넘겨주고 멈춘다.
아래는 스레드 메서드의 요약이다
1 | public void start() 스레드를 별도의 실행 경로에서 시작한다. 이 메서드를 호출하면 스레드가 실행되고, run() 메서드가 호출된다. |
2 | public void run() 스레드가 실행할 코드를 포함하는 메서드. 이 메서드는 별도로 호출하지 않고, start() 메서드를 호출하면 자동으로 실행된다. |
3 | public final void setName(String name) 스레드의 이름을 설정. getName() 메서드를 사용하여 설정된 이름을 확인할 수 있다. |
4 | public final void setPriority(int priority) 스레드의 우선순위를 설정. 우선순위는 1에서 10 사이의 값으로 설정할 수 있다. |
5 | public final void setDaemon(boolean on) 스레드를 데몬 스레드로 설정. true를 매개변수로 전달하면 스레드가 데몬 스레드로 설정된다. |
6 | public final void join(long millisec) 현재 스레드를 일시 중지하고, 지정된 시간 동안 또는 대상 스레드가 종료될 때까지 기다린다. |
7 | public void interrupt() 스레드의 실행을 중단시킨다. 스레드가 일시 중지된 상태에서 계속 실행되도록 한다. |
8 | public final boolean isAlive() 스레드가 아직 실행 중인지 확인한다. 스레드가 시작된 후 아직 종료되지 않았다면 true를 반환한다. |