JAVA/쓰레드

[JAVA 쓰레드 #1] Thread 구현

우엥우아앙 2021. 2. 1. 14:40

Java 쓰레드 구현

Java의 일반 쓰레드 구현 방식은 다음과 같다.

 

  1. Thread 클래스를 상속한 클래스의 객체를 생성과 동시에 구동
  2. Thread 클래스를 상속한 클래스의 객체를 생성한 후 나중에 구동
  3. Runnable 인터페이스를 구현한 클래스의 객체를 생성과 동시에 구동
  4. Runnable 인터페이스를 구현한 클래스의 객체를 생성한 후 나중에 구동
  5. 4번을 inline으로 구현하기
  6. 4번을 anonymous로 구현하기
  7. 4번을 Lambda로 구현하기

 

4번째가 가장 많이 사용되는 방식이다. 그 이유는 다음과 같다.

  • 쓰레드 관련 코드와 테스크 관련 코드의 구현이 분리된다.
  • 쓰레드 생성과 실행 등의 제어가 더 용이하다.

 

5, 6, 7번째의 경우에는 테스트를 별도 클래스가 아닌 함수로 구현하기 때문에 사용할 때 제약이 있다. 대신 UI 이벤트 처리등에 많이 사용된다.

 

1. Thread 객체를 만들자 마자 실행하는 경우

Thread 클래스를 상속하여 쓰레드를 구현할 때는 3가지만 생각하면 된다.

 

  1. Thread class를 상속
  2. public void run()을 구현
  3. start()로 run() 메소드를 호출
package S_20210201;

import java.util.concurrent.TimeUnit;

public class FirstWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		new MyThreadTask();
		Thread mt = new MyThreadTask();
		
		
		System.out.println("Main Thread Ends ...");
	}
}

class MyThreadTask extends Thread{
	private static int count = 0;
	private int id;
	
	@Override
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("<"+id+"> LoopCnt = "+i);
			try {
				TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MyThreadTask() {
		this.id=count++;
		this.start();
	}
}

결과

 

2. Thread 객체를 원하는 시점에 실행하는 경우

run() 메소드를 호출을 Thread 객체를 받는 변수에서 호출하는 것이다.

package S_20210201;

import java.util.concurrent.TimeUnit;

public class SecondWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		new MyThreadTask1().start();;
		Thread mt = new MyThreadTask1();
		mt.start();
		
		System.out.println("Main Thread Ends ...");
	}
}

class MyThreadTask1 extends Thread{
	private static int count = 0;
	private int id;
	
	@Override
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("<"+id+"> LoopCnt = "+i);
			try {
				TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MyThreadTask1() {
		this.id=count++;
//		this.start();
	}
}

결과는 위와 동일하다.

 

3. Runnable 객체를 만들자 마자 실행하는 경우

Runnable 인터페이스를 이용해 쓰레드를 구동할 때는 3가지만 생각하면 된다.

  1. Runnable 인터페이스 구현
  2. public void run()을 구현
  3. Thread(Runnable) 객체를 만들어 start()로 run() 메소드를 호출
package S_20210201;

import java.util.concurrent.TimeUnit;

public class ThirdWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		new MyThreadTask2();
		new MyThreadTask2();
		
		System.out.println("Main Thread Ends ...");
	}
}

class MyThreadTask2 implements Runnable{
	private static int count = 0;
	private int id;
	
	@Override
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("<"+id+"> LoopCnt = "+i);
			try {
				TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MyThreadTask2() {
		this.id=count++;
		new Thread(this).start();
	}
}

결과는 위와 동일하다.

 

4. Runnable 객체를 원하는 시점에 실행하는 경우

run() 메소드를 호출을 Runnable 레퍼런스에서 호출하는 것이다.

package S_20210201;

import java.util.concurrent.TimeUnit;

public class FourthWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		new Thread(new MyThreadTask3()).start();
		Thread tr = new Thread(new MyThreadTask3());
		tr.start();
		
		System.out.println("Main Thread Ends ...");
	}
}

class MyThreadTask3 implements Runnable{
	private static int count = 0;
	private int id;
	
	@Override
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("<"+id+"> LoopCnt = "+i);
			try {
				TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	public MyThreadTask3() {
		this.id=count++;
//		new Thread(this).start();
	}
}

결과는 위와 동일하다.

 

5. Runnable 을 inline으로 구현하기

다음은 Runnable를 별도의 클래스 없이 inline 으로 구현한 것이다. 주로 UI 이벤트 등의 처리에 많이 사용한다.

package S_20210201;

import java.util.concurrent.TimeUnit;

public class FifthWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		Runnable myThreadTask4 = new Runnable() {
			@Override
			public void run() {
				for(int i=0;i<5;i++) {
					System.out.println("LoopCnt = "+i);
					try {
						TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}				
			}
		};
		
		Thread tr = new Thread(myThreadTask4);
		tr.start();
		
		System.out.println("Main Thread Ends ...");
	}
}

 

6. inline Runnable을 Anonymous로 구현

UI event등의 처리에 많이 사용되는 방식

package S_20210201;

import java.util.concurrent.TimeUnit;

public class SixthWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i=0;i<5;i++) {
					System.out.println("LoopCnt = "+i);
					try {
						TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}				
			}
		}).start();
	
		System.out.println("Main Thread Ends ...");
	}
}

결과는 위와 동일하다.

 

7. Runnable Lambda로 구현하기

위의 6번째를 Lambda로 구현한 것이다.

package S_20210201;

import java.util.concurrent.TimeUnit;

public class SeventhWay{
	public static void main(String[] args) throws Exception {
		System.out.println("Main Thread Starts ...");
		
		
		new Thread(()->{
			for(int i=0;i<5;i++) {
				System.out.println("LoopCnt = "+i);
				try {
					TimeUnit.MICROSECONDS.sleep((long)Math.random()*1000);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
		
	
		System.out.println("Main Thread Ends ...");
	}
}

결과는 위와 동일하다.

 

 

참고 블로그 : http://hochulshin.com/java-multithreading-thread-thread-implementation/