쓰레드 생성 방법
- Thread 클래스를 직접 상속받아 생성
- Runnable 인터페이스 구현해서 생성
run() 메소드
를 오버라이딩해서 사용쓰레드 생성 코드 예시
public class Sample extends Thread {
public void run() { // Thread 를 상속하면 run 메서드를 구현해야 한다.
System.out.println("thread run.");
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.start(); // start()로 쓰레드를 실행한다.
}
}
Sample 클래스
가 Thread 클래스
를 상속했다. Thread 클래스의 run 메소드를 구현하면 위 예제와 같이 sample.start()
실행시 sample 객체의 run 메소드가 수행된다.
쓰레드 동작 확인 코드 예시
public class Sample extends Thread {
int seq;
public Sample(int seq) {
this.seq = seq;
}
public void run() {
System.out.println(this.seq + " thread start."); // 쓰레드 시작
try {
Thread.sleep(1000); // 1초 대기한다.
} catch (Exception e) {
}
System.out.println(this.seq + " thread end."); // 쓰레드 종료
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 총 10개의 쓰레드를 생성하여 실행한다.
Thread t = new Sample(i);
t.start();
}
System.out.println("main end."); // main 메소드 종료
}
}
Thread.sleep(1000)
: 시작과 종료 사이에 1초의 간격Thread.sleep((int) (Math.random()*1000)
: 시작과 종료 사이에 n초의 간격.sleep()
은 예외처리가 필요함Runnable 인터페이스
를 이용하여 스레드를 생성쓰레드 생성 코드 예시
- RunnableTest.class
// Runnable 인터페이스 상속
public class RunnableTest implements Runnable
{
// Runnable 인터페이스의 run() 오버라이딩
public void run()
{
try { // 인터럽트 예외처리
for (int i=0 ; i<10 ; i++) {
// 대기시간 0.2초
Thread.sleep(200);
System.out.println("스레드 :" + i);
}
} catch(InterruptedException e ) {
e.printStackTrace();
}
}
}
- Thread2.class
public class Thread2
{
public static void main(String args[])
{
// Runnable 인터페이스 객체생성
RunnableTest Obj1 = new RunnableTest();
RunnableTest Obj2 = new RunnableTest();
// Runnable 객체를 매개변수로 하여 스레드 객체 th생성
Thread th1 = new Thread(Obj1);
Thread th2 = new Thread(Obj2);
th1.start();
th2.start();
}
}
공유객체 생성 방법
- 공통으로 사용할 데이터를 클래스로 정의한다.
- 공통으로 사용할 클래스의 인스턴스를 만든다.
- 이 인스턴스를 각각의 쓰레드에 넘겨 준다.
- 각각의 쓰레드는 이 인스턴스의 참조값을 저장한 변수를 이용하여 공통 데이터를 사용한다.
공유객체 예시
- 공유객체 MusicBox.class
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();
}
}
}
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();
}
}
}
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();
}
}
}
}
- 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;
}
}
}
- MusicBox와 MusicPlayer를 이용하는 MusicBoxExam 클래스
public class MusicBoxExam1 {
public static void main(String[] args) {
// MusicBox 인스턴스
MusicBox box = new MusicBox();
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();
}
}
synchronized
키워드를 사용하여 임계영억을 지정하며, 동시에 공유자원을 차지하지 않도록 강제한다.synchronized
가 붙어 있을 경우 먼저 호출한 메소드가 객체의 사용권(Monitoring Lock
)을 얻는다.public synchronized void playMusicA(){
for(int i = 0; i < 10; i ++){
System.out.println("신나는 음악!!!");
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void playMusicB(){
for(int i = 0; i < 10; i ++){
synchronized(this){
System.out.println("슬픈 음악!!!");
}
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized
를 붙혀서 실행해 보면, 메소드 하나가 모두 실행된 후에 다음 메소드가 실행된다.wait()
와 같은 메소드를 만나기 전까지 유지된다.synchronized
를 붙히지 않은 메소드는 다른 쓰레드들이 synchronized
메소드를 실행하면서 모니터링 락
을 획득했다 하더라도, 그것과 상관없이 실행된다.synchronized
를 메소드에 붙혀서 사용 할 경우, 메소드의 코드가 길어지면, 마지막에 대기하는 쓰레드가 너무 오래 기다리는것을 막기위해서 메소드에 synchronized
를 붙이지 않고, 문제가 있을것 같은 부분만 synchronized
블록을 사용한다.Thread.sleep()
이나 Object가 가지고 있는 wait()
메소드가 호출이 되면 쓰레드는 Blocked 상태가 된다.Thread.sleep()
은 특정시간이 지나면 자신 스스로 블록상태에서 빠져나와 Runnable이나 Running상태가 된다.wait()
메소드는 다른 쓰레드가 notify()
나 notifyAll()
메소드를 호출하기 전에는 블록상태에서 해제되지 않는다.wait()
메소드는 호출이 되면 모니터링 락을 놓게 된다. 그래서 대기중인 다른 메소드가 실행한다.yeild
메소드가 호출되면 해당 쓰레드는 다른 쓰레드에게 자원을 양보하게 된다.join
메소드를 호출하게 되면 해당 쓰레드가 종료될 때까지 대기하게 된다.해당 쓰레드가 모든 일을 끝낼 동안 다른 쓰레드가 기다려준다.
public class JoinExam {
public static void main(String[] args) {
MyThread5 thread = new MyThread5();
// Thread 시작
thread.start();
System.out.println("Thread가 종료될때까지 기다립니다.");
try {
// 해당 쓰레드가 멈출때까지 멈춤
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread가 종료되었습니다.");
}
}
wait
와 notify
는 동기화된 블록안에서 사용해야 한다. wait
를 만나게 되면 해당 쓰레드는 해당 객체의 모니터링 락에 대한 권한을 가지고 있다면 모니터링 락의 권한을 놓고 대기한다.// Runnable을 구현하는 DaemonThread클래스를 작성
public class DaemonThread implements Runnable {
// 무한루프안에서 0.5초씩 쉬면서 데몬쓰레드가 실행중입니다를 출력하도록 run()메소드를 작성
@Override
public void run() {
while (true) {
System.out.println("데몬 쓰레드가 실행중입니다.");
try {
Thread.sleep(500);
} 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("메인 쓰레드가 종료됩니다. ");
}
}