Thread는 무엇인가? OS적 관점과 자바에서 다루는 쓰레드에 대해알아보자!
OS에서 process, thread에 대한 기본 지식은 생략한다.
public class MyThread1 extends Thread {
String str;
public MyThread1(String str){
this.str = str;
}
public void run(){
for(int i = 0; i < 10; i ++){
System.out.print(str);
try {
// 너무 빠르기 때문에 수행결과를 잘 확인 할 수 없어서 Thread.sleep() 메서드를 이용해서 조금씩
// 쉬었다가 출력할 수 있게한다.
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExam1 {
public static void main(String[] args) {
// MyThread인스턴스를 2개 만듭니다.
MyThread1 t1 = new MyThread1("*");
MyThread1 t2 = new MyThread1("-");
t1.start();
t2.start();
System.out.print("!!!!!");
}
}
public class MyThread2 implements Runnable {
String str;
public MyThread2(String str){
this.str = str;
}
@Override
public void run(){
for(int i = 0; i < 10; i ++){
System.out.print(str);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExam2 {
public static void main(String[] args) {
MyThread2 r1 = new MyThread2("*");
MyThread2 r2 = new MyThread2("-");
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
System.out.print("!!!!!");
}
}
public class Bus extends Car implements Runnable
public class MusicBox {
//신나는 음악!!! 이란 메시지가 1초이하로 쉬면서 10번 반복출력
public void playMusicA(){
for(int i = 0; i < 10; i ++){
System.out.println("신나는 음악!!!");
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
} // for
} //playMusicA
//슬픈 음악!!!이란 메시지가 1초이하로 쉬면서 10번 반복출력
public void playMusicB(){
for(int i = 0; i < 10; i ++){
System.out.println("슬픈 음악!!!");
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
} // for
} //playMusicB
//카페 음악!!! 이란 메시지가 1초이하로 쉬면서 10번 반복출력
public void playMusicC(){
for(int i = 0; i < 10; i ++){
System.out.println("카페 음악!!!");
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
} // for
} //playMusicC
}
// MusicBox를 가지는 Thread객체 MusicPlayer
public class MusicPlayer extends Thread{
int type;
MusicBox musicBox; // "공유 객체"
// 생성자로 부터 musicBox와 정수를 하나 받아들여서 필드를 초기화
public MusicPlayer(int type, MusicBox musicBox){
this.type = type;
this.musicBox = musicBox;
}
// type이 무엇이냐에 따라서 musicBox가 가지고 있는 메소드가 다르게 호출
public void run(){
switch(type){
case 1 : musicBox.playMusicA(); break;
case 2 : musicBox.playMusicB(); break;
case 3 : musicBox.playMusicC(); break;
}
}
}
// 메인 쓰레드가 있는 클래스로 위 클래스 인스턴스를 쓰레드화
public class MusicBoxExam1 {
public static void main(String[] args) {
// 공유 객체로 쓸 MusicBox 인스턴스
MusicBox box = new MusicBox();
// MusicPlayer object는 모두 동일한 MusicBox obj를 가진다
MusicPlayer kim = new MusicPlayer(1, box);
MusicPlayer lee = new MusicPlayer(2, box);
MusicPlayer kang = new MusicPlayer(3, box);
// MusicPlayer쓰레드를 실행합니다.
kim.start();
lee.start();
kang.start();
}
}
public void playMusicB(){
for(int i = 0; i < 10; i ++){
// 해당 객체에 대해 모니터링 락가졌니? (여기서는 this)
synchronized (this) {
System.out.println("슬픈 음악!!!");
}
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
} // for
} //playMusicB
public class MyThread5 extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("MyThread5 : "+ i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} // run
}
// 위 해당 쓰레드를 실행하고, 해당쓰레드가 종료될때까지 기다린 후, 내용을 출력하는 JoinExam클래스
public class JoinExam {
public static void main(String[] args) {
/*
MyThread5 thread = new MyThread5();
thread.start();
System.out.println("Thread 시작.");
System.out.println("Thread 종료.");
*/
MyThread5 thread = new MyThread5();
// Thread 시작 -> (Thread 상속 받았기 때문에 바로 시작 가능)
thread.start();
System.out.println("Thread가 종료될때까지 기다립니다.");
try {
// 해당 쓰레드가 멈출때까지 멈춤
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread가 종료되었습니다.");
}
}
[ 주석 안 코드 먼저 ]
Thread 시작.
Thread 종료.
MyThread5 : 0
MyThread5 : 1
MyThread5 : 2
MyThread5 : 3
MyThread5 : 4
[ join이 들어간 메인 코드 ]
Thread가 시작되었습니다.
Thread가 종료될때까지 기다립니다.
MyThread5 : 0
MyThread5 : 1
MyThread5 : 2
MyThread5 : 3
MyThread5 : 4
Thread가 종료되었습니다.
public class ThreadB extends Thread{
// 해당 쓰레드가 실행되면 자기 자신의 모니터링 락을 획득
// 5번 반복하면서 0.5초씩 쉬면서 total에 값을 누적
// 그후에 notify()메소드를 호출하여 wiat하고 있는 쓰레드를 깨움
int total;
@Override
public void run(){
synchronized(this){ // 동기화 블럭!!
for(int i=0; i<5 ; i++){
System.out.println(i + "를 더합니다.");
total += i;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify(); // 알린다! -> wait 풀어 임마!
}
}
}
// ThreadB를 사용하며 wait하는 클래스 작성
public class ThreadA {
public static void main(String[] args){
// 앞에서 만든 쓰레드 B를 만든 후 start
// 해당 쓰레드가 실행되면, 해당 쓰레드는 run메소드 안에서 자신의 모니터링 락을 획득
ThreadB b = new ThreadB();
b.start();
// b에 (thread 상속받는 object) 대하여 동기화 블럭을 설정
// 만약 main쓰레드가 아래의 블록을 위의 Thread보다 먼저 실행되었다면 wait를 하게 되면서 모니터링 락을 놓고 대기
synchronized(b){
try{
// b.wait()메소드를 호출.
// 메인쓰레드는 정지
// ThreadB가 5번 값을 더한 후 notify를 호출하게 되면 wait에서 깨어남
System.out.println("b가 완료될때까지 기다립니다.");
b.wait(); // 잠들어서 기다려 -> 즉 다른 쓰레드가 notify 할때까지 기다린다!
}catch(InterruptedException e){
e.printStackTrace();
}
//깨어난 후 결과를 출력
System.out.println("Total is: " + b.total);
}
}
}
b가 완료될때까지 기다립니다.
0를 더합니다.
1를 더합니다.
2를 더합니다.
3를 더합니다.
4를 더합니다.
Total is: 10
// Runnable을 구현하는 DaemonThread클래스를 작성
public class DaemonThread implements Runnable {
// 무한루프안에서 0.5초씩 쉬면서 데몬쓰레드가 실행중입니다를 출력하도록 run()메소드를 작성
@Override
public void run() {
while (true) {
System.out.println("데몬 쓰레드가 실행중입니다.");
try {
Thread.sleep(500); // 0.5초씩 쉬면서
} catch (InterruptedException e) {
e.printStackTrace();
break; // Exception발생시 while 문 빠찌도록
}
}
}
public static void main(String[] args) {
// Runnable을 구현하는 DaemonThread를 실행하기 위하여 Thread 생성
Thread th = new Thread(new DaemonThread());
th.setDaemon(true); // 데몬쓰레드로 설정!!
th.start(); // 쓰레드를 실행
// 메인 쓰레드가 1초뒤에 종료되도록 설정.
// 데몬쓰레드는 다른 쓰레드가 모두 종료되면 자동종료.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("메인 쓰레드가 종료됩니다. ");
}
}