일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- jwt
- TS
- ES5
- 으
- es6
- 서버리스 #
- 데이터베이스 #try #이중
- github
- entity
- 당근마켓
- 클론코딩
- literal
- ZOOM
- 1px border
- 문서번호
- font-size
- 타입스크립트
- 컴포넌튼
- 0.25px border
- &연산
- 0.75px border
- Props
- angular
- Strict
- 0.5px border
- npm
- Websocket
- 전역변수
- 10px
- TypeScript
- Today
- Total
복잡한뇌구조마냥
[JAVA] 쓰레드 ( Thread ) 본문
운영체제 ( OS )
- 컴퓨터의 하드웨어를 사용하게 해주는 프로그램
프로세스 ( Process )
- 현재 실행중인 프로그램
쓰레드 ( Thread )
여러가지 작업을 동시에 수행할 수 있게 하는 것
- 자바 프로그램은 JVM에 의해 실행됨
- 자바 프로그램이 여러개의 작업을 동시에 하게 만들려면 Thread를 알아야함.
- 운영체제에서 프로세스가 여러개 동작하듯이 하나의 프로세스에서도 여러개의 동작을 할 수 있음.
- 여러개의 동작 각각을 Thread라고 생각하면 됨.
쓰레드 생성
- 자바에서 Thread를 만드는 방법은 크게 Thread 클래스를 상속받는 방법과 Runnable인터페이스를 구현하는 방법이 있음.
extend Thread
- Thread 클래스를 상속받는 방법
// MyThread1.java
package Thread;
public class MyThread1 extends Thread {
String str;
public MyThread1(String str){
this.str = str;
}
@Override
public void run() {
super.run();
for (int i = 0; i< 10; i++){
System.out.println(str);
try {
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- Thread 클래스가 가지고 있는 run 클래스를 오버라이딩해서 동작 정의
// ThreadExam1.java
package Thread;
public class ThreadExam1 {
public static void main(String[] args) {
MyThread1 mth1 = new MyThread1("*");
MyThread1 mth2 = new MyThread1("-");
mth1.start();
mth2.start();
System.out.println("끝!!!");
}
}
- Thread 클래스를 상속받은클래스를 선언하고 run이 아닌 start 메소드를 호출하여 동작함
- console에서 보듯이 main 함수는 끝났는데 Thread 클래스를 상속받은 클래스들이 동작하고 있는것을 볼 수 있음.
- 즉, Thread는 main과 별도로 기능이 동작하고 있음.
- 내부의 Thread들이 동작이 끝나야 프로세스가 종료됨.
implements Runnable
- Runnable 인터페이스를 구현하는 방법
package Thread;
public class MyThread2 implements Runnable {
String str;
MyThread2(String str){
this.str = str;
}
@Override
public void run() {
for (int i = 0; i< 10; i++){
System.out.println(str);
}
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- Runnable 인터페이스를 구현하도록 만들어줬음.
- Runnable 에서 run 메소드를 가지고있음.
- Runnable 인터페이스를 이용하는 방법은 이용하는 이유는 클래스가 단일 상속만 가능하기 때문임.
package Thread;
public class ThreadExam2 {
public static void main(String[] args) {
MyThread2 mth1 = new MyThread2("**");
MyThread2 mth2 = new MyThread2("--");
Thread t1 = new Thread(mth1);
Thread t2 = new Thread(mth2);
t1.start();
t2.start();
System.out.println("끝 !!!");
}
}
- Runnable 인터페이스는 start 메소드를 가지고 있지 않기 때문에, Thread 생성자를 통해서 넣어줘야 사용가능
- Thread를 상속받았을 때와 마찬가지로, main 클래스가 끝나는 것과 별개로 Thread가 수행됨.
쓰레드와 공유객체
- 하나의 객체를 여러개의 Thread가 사용하기
package Thread;
public class MusicBox {
private String MusicA = "신나는 음악";
private String MusicB = "슬픈 음악";
private String MusicC = "카페 음악";
public void playMusicA(){
for (int i = 0; i< 10; i++){
System.out.println(MusicA);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void playMusicB(){
for (int i = 0; i< 10; i++){
System.out.println(MusicB);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void playMusicC(){
for (int i = 0; i< 10; i++){
System.out.println(MusicC);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// MusicPlayer.java
package Thread;
public class MusicPlayer extends Thread{
int type;
MusicBox mb;
public MusicPlayer(int type, MusicBox mb){
this.type = type;
this.mb = mb;
}
@Override
public void run(){
switch (type){
case 1: mb.playMusicA(); break;
case 2: mb.playMusicB(); break;
case 3: mb.playMusicC(); break;
default: break;
}
}
}
// MusicBoxExam1.java
package Thread;
public class MusicBoxExam1 {
public static void main(String[] args) {
MusicBox mb = new MusicBox();
MusicPlayer mp1 = new MusicPlayer(1, mb);
MusicPlayer mp2 = new MusicPlayer(2, mb);
MusicPlayer mp3 = new MusicPlayer(3, mb);
mp1.start();
mp2.start();
mp3.start();
}
}
- 특정 객체를 공유하여 각각의 Thread가 다른 역할을 하도록 만들 수 있음.
동기화 메소드와 동기화 블록
- 공유객체의 메소드가 동시에 호출되서 공유객체가 고장날 수 있음
- 공유객체가 가진 메소드를 동시에 호출되지 않도록 해야함.
- 메소드 앞에 synchronized를 붙이기.
- 여러개의 Thread들이 공유객체의 메소드를 사용할 때 메소드에 synchronized가 붙어 있을 경우 호출한 메소드가 객체의 사용권(Monitoring Lock)을 얻음
package Thread;
public class MusicBox {
private String MusicA = "신나는 음악";
private String MusicB = "슬픈 음악";
private String MusicC = "카페 음악";
public synchronized void playMusicA(){
for (int i = 0; i< 10; i++){
System.out.println(MusicA);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized void playMusicB(){
for (int i = 0; i< 10; i++){
System.out.println(MusicB);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized void playMusicC(){
for (int i = 0; i< 10; i++){
System.out.println(MusicC);
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 동일한 코드에 synchronized를 붙여서 실행하면, 메소드 하나가 모두 실행된 후에 다음 메소드가 실행됨
- 해당 모니터링 락은 메소드 실행이 종료되거나, wait()같은 메소드를 만나기 전까지 유지됨.
- 다른 쓰레드들이 모니터링 락을 놓을때까지 대기
- synchronized를 붙이지 않은 메소드는 다른 쓰레드들이 synchronized 메소드를 실행하면서 모니터랑 락을 하더라도 상관없이 실행함.
public void playMusicC(){
for (int i = 0; i< 10; i++){
synchronized (this){
System.out.println(MusicC);
}
try{
Thread.sleep((int)(Math.random() *1000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- synchronized를 메소드에 붙여서 사용할 경우, 메소드의 코드가 길어지면, 마지막에 대기하는 쓰레드가 너무 오래 기다림
- 메소드 전체가 아닌 부분만 synchronized로 동기화 시킬 수 있음.
- 메소드에 synchronized를 붙이지 않고, 문제가 있을 것 같은 부분만 synchronized 블록을 사용함
- 백그라운드로 동작이 필요한 기능에는 synchronized를 빼고 사용하면 된다고 이해하면 됨.
쓰레드 상태제어
상태
- New Thread는 쓰레드가 생성되는 상태
- Runnable은 동작 가능한 상태
- Running은 실행중인 상태
- Block은 일시 정지된 상태
- Dead는 쓰레드의 run 메소드가 종료된 상태
메소드
- 삭선 표시된 메소드는 deprecated 된 메소드이므로 사용하지 않는게 좋음.
- sleep 메소드는 특정시간이 지나면 block 상태가 해제
- wait 메소드가 되면 모니터링 락을 놓게됨.
- wait 메소드는 noitify나 notifyAll이 동작되면 wait이 종료됨.
- yield 메소드가 호출되면 thread는 다른 thread에게 자원을 양보
- join 메소드가 호출되면 해당 쓰레드가 종료될 때까지 대기함.
// MyThread3.java
package Thread;
public class MyThread3 extends Thread {
@Override
public void run() {
for (int i = 0; i< 5; i++){
System.out.println("MyThread3 : " + i);
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// ThreadExam3
package Thread;
public class ThreadExam3 {
public static void main(String[] args) {
MyThread3 mth1 = new MyThread3();
mth1.start();
System.out.println("시작");
try {
mth1.join();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("종료!");
}
}
- 실행 결과를 비교하면 join을 사용하면 thread가 종료될 때까지 기다리는 것을 확인 할 수 있음.
// MyThread4.java
package Thread;
public class MyThread4 extends Thread {
int total = 0;
@Override
public void run() {
synchronized (this){
for(int i = 0; i<5; i++){
System.out.println(i + "를 더합니다.");
total += i;
try{
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
notify();
}
}
}
ThreadExam4.java
package Thread;
public class ThreadExam4 {
public static void main(String[] args) {
MyThread4 mth1 = new MyThread4();
mth1.start();
synchronized (mth1){
try{
System.out.println("완료될때까지 대기");
mth1.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(mth1.total);
}
}
}
- wait이 없을 때는 메인스레드가 먼저 동작하므로 total이 계산하기 전 값이 나옴.
- wait이 있기 때문에 메인스레드가 notify를 만나기 전까지 정지함.
- 스레드가 모든 값을 더하면 notify가 실행되면서 main 스레드가 동작함
데몬스레드 ( Daemon Thread )
- 자바에서 데몬과 유사하게 동작하는 쓰레드
- 자바프로그램을 만들 때, 백그라운드에서 특별한 작업을 처리하게 하는 용도로 만듬.
- 데몬쓰레드는 일반 쓰레드(main 등)가 모두 종료되면 강제적으로 종료되는 특징을 가지고 있음.
* 데몬(Daemon)
- 리눅스, 유닉스 계열의 운영체제에서 백그라운드로 동작하는 프로그램
- 윈도우에서는 주로 서비스 라고 이야기함.
package Thread;
public class DaemonThread implements Runnable {
@Override
public void run() {
while (true){
System.out.println("데몬 쓰레드가 실행중입니다.");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
Thread t = new Thread(new DaemonThread());
t.setDaemon(true);
t.start();
try {
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("메인쓰레드가 종료됩니다.");
}
}
- 무한루프로 0.5초마다 문구가 출력되는 메소드 생성
- 데몬쓰레드 적용을 위해서 setDaemon() 메소드를 사용해서 적용해야함.
- 메인 쓰레드가 종료되면 데몬쓰레드도 종료됨.
'BE > JAVA' 카테고리의 다른 글
[JAVA] n진수 변환, 변환 정리 (String ↔ 숫자형) + 3진법 뒤집기 (0) | 2025.07.03 |
---|---|
[JAVA] 람다 ( 람다식 ) (0) | 2025.06.18 |
[JAVA] 어노테이션 (Annotation) (1) | 2025.06.17 |
[JAVA] 자바 IO ( Byte 단위, 다양한 타입, Char 단위 입출력) (0) | 2025.06.14 |
[JAVA] 날짜와 시간 ( Date, Calendar, java.time ) (2) | 2025.06.14 |