
프로그램을 실행하면 OS로 부터 실행에 필요한 자원을 할당받아 프로세스가 됨
프로세스 : 실행 중인 프로그램 ( 수행에 필요한 자원 + 쓰레드)
둘 이상의 쓰레드를 가진 프로세스 -> 멀티쓰레드 프로세스
쓰레드 : 프로세스라는 작업공간에서 작업을 처리하는 일꾼
반면 멀티쓰레드 프로세스는 여러 쓰레드가 같은 프로세스 내에서 자원을 공유하면서 작업을 하기에 동기화, 교착상태 같은 문제를 고려해야함
교착상태 : 두 쓰레드가 자원을 점유한 상태에서 서로 상대편이 점유한 자원을 사용하려고 기다리느라 진행이 멈추는 상태
class MyThread extends Thread{
public void run() { /* 작업 내용 */} // Thread클래스의 run() 오버라이딩
}
class MyThread extends Runnable{
public void run() { /* 작업 내용 */} // Runnable인터페이스의 run() 오버라이딩
}
단순히 쓰레드를 통해 하고자하는 내용으로 run()의 몸통{}을 채우는 것!
package ch13;
public class Ex13_1 {
public static void main(String[] args) {
ThreadEx1_1 t1 = new ThreadEx1_1();
Runnable r = new ThreadEx1_2();
/* Runnable인터페이스를 구현한 클래스의 인스턴스 생성 후 ,
생성된 인스턴스를 Thread클래스의 생성자에 매개변수로 제공해야 한다.*/
Thread t2 = new Thread(r); // 생성자 Thread(Runnable Target)
t1.start();
t2.start();
}
}
class ThreadEx1_1 extends Thread{
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println(this.getName()); // 조상인 Thread의 getName() 호출.
}
}
}
class ThreadEx1_2 implements Runnable{
// 쓰레드가 수행할 작업
public void run() {
for(int i =0; i< 5; i++) {
// Thread.currentThread() -> 현재 실행중인 Thread의 참조를 반환한다.
// getName -> 쓰레드의 이름 반환.
System.out.println(Thread.currentThread().getName());
}
}
}
start() 호출을 통해 실행
-> 호출하였다고 바로 실행되는 것이 아니라, 실행 대기 상태에 있다가 자신의 차례가 되어야 실행됨(실행 대기중 쓰레드 없다면 바로 실행)
쓰레드 실행 순서 -> OS의 스케쥴러가 작성한 스케쥴에 의해 실행
쓰레드를 한번더 실행하려면 -> 새로 쓰레드를 생성한 후 호출해야함!!
ThreadEx1_1 t1 = new ThreadEx1_1();
t1.start();
t1 = new ThreadEx1_1();
t1.start();
run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것

즉 서로 독립적인 공간에서 작업을 수행하는 것을 볼 수 있다
main메서드의 코드를 실행하는 쓰레드
프로그램 실행 시 기본적으로 하나의 쓰레드를 생성한 후 , 그 쓰레드가 main메서드를 호출해서 작업이 실행되도록 함

쓰레드 종류 : 사용자 쓰레드 & 데몬 쓰레드
실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다
하나의 쓰레드로 작업 -> 한 작업을 마친 후에 다른 작업 수행
두개의 쓰레드로 작업 -> 짧은 시간동안 두개의 쓰레드 번갈아 가면서 실행

package ch13;
public class Ex13_2 {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(int i = 0; i < 300; i++)
System.out.printf("%s", new String("-"));
System.out.print("소요시간1: " + (System.currentTimeMillis() - startTime));
for(int i = 0; i < 300; i++)
System.out.printf("%s", new String("|"));
System.out.print("소요시간2:" + (System.currentTimeMillis() - startTime));
}
}

싱글쓰레드 와 멀티쓰레드 각각 작업 시간은 거의 비슷
-> 오히려 멀티쓰레드가 작업전환(Context switching) 때문에 시간이 더 걸림 + 한 쓰레드가 화면에 출력하고 있는 동안 다른 쓰레드는 출력이 끝나길 기다리며 발생하는 대기 시간
즉 싱글 코어에서 단순히 CPU만을 사용하는 계산작업이라면 오히려 싱글 쓰레드가 효율적
package ch13;
public class Ex13_3 {
static long startTime = 0;
public static void main(String[] args) {
ThreadEx3_1 th1 = new ThreadEx3_1();
th1.start();
startTime = System.currentTimeMillis();
for(int i = 0; i< 300; i++)
System.out.printf("%s", new String("-"));
System.out.print("소요시간1:" + (System.currentTimeMillis()- Ex13_3.startTime));
}
}
class ThreadEx3_1 extends Thread{
public void run() {
for(int i=0; i< 300; i++)
System.out.printf("%s", new String("|"));
System.out.print("소요시간2:" + (System.currentTimeMillis() - Ex13_3.startTime));
}
}

멀티코어에서 멀티 쓰레드로 작업 수행 시 두 작업이 겹치는 부분이 발생
-> 화면(console)이라는 자원을 두고 두 쓰레드가 경쟁하게 된다
I/O블락킹 : 쓰레드가 입출력 처리를 위해 기다리는 것
두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우 멀티쓰레드가 효율적
ex) 사용자로부터 데이터 입력 받는 작업, 네트워크로 파일 주고 받음 등

사용자로 부터 입력을 받는 작업(A)과 화면에 출력하는 작업(B)을 하나의 쓰레드에서 처리한다면 사용자가 입력을 마칠때 까지 아무 일도 하지 못함
package ch13;
import javax.swing.JOptionPane;
public class Ex13_4 {
public static void main(String[] args) throws Exception{
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
for(int i=10; i>0; i--)
{
System.out.println(i);
try {
Thread.sleep(1000); // 1초간 시간 지연.
} catch(Exception e) {}
}
}
}

입력하신 값은 hello입니다.
10
9
8
7
6
5
4
3
2
1
package ch13;
import javax.swing.JOptionPane;
public class Ex13_5 {
public static void main(String[] args) {
ThreadEx5_1 th1 = new ThreadEx5_1();
th1.start();
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
}
}
class ThreadEx5_1 extends Thread{
public void run() {
for(int i = 10; i >0; i--) {
System.out.println(i);
try {
sleep(1000);
} catch(Exception e) {}
}
}
}
숫자 출력 사이의 텀에 입력을 요청함!!
사용자가 입력을 마치지 않아도 화면에 계속 숫자가 출력되고 있음
10
9
8
7
입력하신 값은 hello입니다.
6
5
4
3
2
1
쓰레드는 우선순위(1~10)라는멤버변수를 가짐
우선 순위가 높을수록 더 많은 작업 시간 갖도록함
ex) 파일전송이 있는 메신저의 경우, 파일 다운로드 처리보다 `채팅 내용을 전송하는 쓰레드의 우선순위가 높아야 채팅에 불편함이 없지만 다운로드 오래걸림
-> 보통 시각적인 부분이나 사용자에게 빠르게 반응해야 하는 작업에 우선순위를 높게 부여함
main 쓰레드는 우선순위가 5
void setPrioirty(int newPriority)
int getPriority()
public static final int MAX_PRIORITY = 10 // 최대 우선 순위
public static final int MIN_PRIORITY = 1 // 최대 우선 순위
public static final int NORM_PRIORITY = 5 // 최대 우선 순위
package ch13;
public class Ex13_6 {
public static void main(String[] args) {
ThreadEx6_1 th1 = new ThreadEx6_1();
ThreadEx6_2 th2 = new ThreadEx6_2();
//설정 전에는 main메서드에서 생성하였기에 우선순위 5를 상속받음!!
th2.setPriority(7);
System.out.println("Priority of th1(-) : " + th1.getPriority());
System.out.println("Priority of th2(|) : " + th2.getPriority());
th1.start();
th2.start();
}
}
class ThreadEx6_1 extends Thread{
public void run() {
for(int i = 0; i < 300; i++) {
System.out.print("-");
// 작업 지연 for 문(우선순위가 높으면 한번에 작업을 끝낼수도 있기에)
for(int x = 0; x < 10000000; x++);
}
}
}
class ThreadEx6_2 extends Thread{
public void run() {
for(int i = 0; i < 300; i++) {
System.out.print("|");
for(int x=0; x< 10000000l; x++);
}
}
}
Priority of th1(-) : 5
Priority of th2(|) : 7
-|-||||||||||||||||||||||||||||||||||||||||
|||||||||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||
||||-|------
----------------------------------|||||||||||||||||||||||||---------|||||||||||||||||||||||||||||||||
||||||
||||||||||||||||---------
----------------------|||||||||||||----------------||----|-|-
||||||||||||||||||||||||||||||||||||
||||||||||||||||||||||||||||||||||||||--|||||||||------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
멀티코어에서는 쓰레드의 우선순위에 따른 차이가 거의 없다!
-> 우선 순위를 높게 준다고 더 많은 실행시간과 기회를 갖게 될 것이라 기대하기 어려움
굳이 우선순위에 차등을 주어 쓰레드 실행하려면 특정 OS 스케쥴링 정책과 JVM의 구현을 확인해 보아야함 -> 해도 어느정도 예측만 가능
쓰레드 그룹은 서로 관련된 쓰레드를 그룹으로 다루기 위한 것으로, 폴더를 생성해서 관련된 파일들을 함께 넣어서 관리하는 것처럼 쓰레드 그룹을 생성해서 쓰레드를 그룹으로 묶어서 관리할 수 있다.
Thread의 생성자 이용
Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, String name)
모든 쓰레드는 반드시 쓰레드 그룹에 포함되어야 함
-> 일반적으로 자신을 생성한 쓰레드에 포함됨
Thread의 쓰레드 그룹과 관련된 메서드
// 쓰레드가 자신이 속한 쓰레드 그룹 반환
ThreadGroup getThreadGroup()
// 처리 되지 않은 예외에 의해 쓰레드 그룹의 쓰레드가 실행이 종료 되면
// JVM에 의해 이 메서드가 자동으로 호출
void uncaughtException(Thread t, Throwalbe e)

다른 일반 쓰레드의 작업을 돕는 보조적인 역할
일반 쓰레드가 모두 종료되면 데몬 쓰레드는 자동 종료됨
ex) 가비지 컬렉터, 워드프로세서 자동 저장
무한루프와 조건물을 이용 해 실행 후 대기하고 있다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성
public void run(){
while(true){
try{
Thread.slee(3 * 1000) // 3초마다
} catch(InterruptedException e){}
// autoSave의 값이 true이면 autoSave()를 호출
if(autoSave) autoSave();
}
}
일반 쓰레드의 작성방법과 실행 방법이 같으나 실행 전 setDaemon(true)를 호출하면 됨
package ch13;
class Ex13_7 implements Runnable{
static boolean autoSave = false;
public static void main(String[] args) {
// run메서드를 스레드의 생성자에 전달하라는 뜻으로 볼수있다
Thread t = new Thread(new Ex13_7()); // Thread(Runnable r);
t.setDaemon(true); // 이 부분이 없으면 종료 되지 않는다!
t.start();
for(int i = 1; i <= 10; i++)
{
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
System.out.println(i);
//5초전엔 false
if(i==5) autoSave = true;
}
System.out.println("프로그램을 종료합니다. ");
}
public void run() {
while(true) {
try {
Thread.sleep(3* 1000); // 3초마다
} catch(InterruptedException e) {}
// autoSave의 값이 true이면 autoSave()를 호출한다.
if(autoSave) autoSave();
}
}
public void autoSave() {
System.out.println("작업파일이 자동저장되었습니다.");
}
}
1
2
3
4
5
작업파일이 자동저장되었습니다.
6
7
8
작업파일이 자동저장되었습니다.
9
10
프로그램을 종료합니다.
메인쓰레드와 데몬 쓰레드 존재하는 것을 볼 수 있다