* 쓰레드 & 프로세스
- 프로세스란 '실행중인 프로그램'이며 쓰레드는 프로세스를 구성하는 '제어의 흐름'이다
- 모든 프로세스에는 최소한 하나 이상의 쓰레드가 존재한다
* 쓰레드 (Thread) 의 장점
- 프로세스의 공통 Resource를 공유하므로 '경량 프로세스'로 불린다
* 쓰레드 (Thread) 와 프로세스 (Process) 의 차이
- Process : 프로그램의 '실행단위'
- Thread : Process를 구성하는 '작업단위'
* 자바는 멀티쓰레드가 가능하다
- 하나의 프로세스 안에서 여러개의 쓰레드가 동시 작업하는 멀티 쓰레드
- main메소드(프로그램 진입점)을 가진 자바 프로그램은 하나의 스레드를 가진(main쓰레드) 단일 쓰레드이고
- Thread 클래스나 runnable 인터페이스를 상속받아 다른 쓰레드를 동작 시킬 수 있다
ex) for문 안에 for문 (2중 포문), while문 안에 while문 등등 멀티쓰레드가 가능
* 쓰레드 (Thread) 생성 방법 (실행 방법 Thread객체.start())
- java.lang.Thread 클래스 상속
ㄴ 쓰레드를 상속받는 클래스를 작성
ㄴ run() 메소드를 오버라이딩하여 내용부를 구현한다
ㄴ main() 메소드 내부에서 쓰레드를 상속받은 클래스의 객체를 생성
ㄴ 해당 객체의 start() 메소드를 호출한다
class D extends Thread{
D() {
start();
int i = 0;
while(true) {
System.out.println("i: " + i++);
try {
Thread.sleep(1000);
} catch(Exception e) {}
}
}
public void run() {
int j = 0;
while(true) {
System.out.println("j: " + j++);
try {
Thread.sleep(1000);
} catch(Exception e) {}
}
}
public static void main(String[] args) {
new D();
}
}
- java.lang.Runnable 인터페이스 상속
ㄴ Runnable을 구현하는 클래스 작성
ㄴ run() 메소드를 오버라이딩하여 내용부를 구현한다
ㄴ main() 메소드에서 Runnable을 구현한 클래스의 객체를 생성한다
ㄴ Thread 객체를 생성하여 매개변수로 위의 객체를 대입한다
ㄴ Thread 객체의 start()메소드를 호출한다
class E implements Runnable{
E() {
new Thread(this).start();
int i = 0;
while(true) {
System.out.println("ii: " + i++);
try {
Thread.sleep(1000);
} catch(Exception e) {}
}
}
public void run() {
int j = 0;
while(true) {
System.out.println("jj: " + j++);
try {
Thread.sleep(1000);
} catch(Exception e) {}
}
}
public static void main(String[] args) {
new E();
}
}
* 쓰레드 (Thread) 의 주요 메소드
- start() : 스레드가 수행을 시작하도록 한다.이때 자바가상머신은 이 스레드의 run()메서드를 호출한다
- static int activeCount() : 현재 활성화된 스레드의 수를 리턴한다
- static Thread currentThread() : 현재 실행중인 스레드를 리턴한다
- setName(String name) : 스레드의 이름을 주어진 이름으로 설정한다
- String getName() : 스레드의 이름을 리턴한다
- sleep(long millis) : 현재 실행중인 스레드를 밀리초동안 sleep시킨다
- static yield() : 현재 실행중인 스레드가 점유한 CPU를 내놓도록한다
즉,자기와 우선순위가 같거나 높은 스레드에게 실행기회를 준다.
- setPriority(int priorty) : 스레드의 우선순위를 설정한다
- int getPriority() : 스레드의 우선 순위를 반환 한다
- join() : 스레드가 종료 될때 까지 기다린다. 데몬 스레드의 강제 종료를 막기 위해 join()메서드를 호출하면
메인 스레드가 종료되어도 데몬 스레드가 완료되어야 종료 된다
- wait() : 현재 스레드를 대기 상태로 만든다.
- notify() : wait()로 대기 상태가 된 스레드를 다시 runnable상태로 만든다.
* 쓰레드 (Thread) 의 주요 상수
- 스레드에 할당 할 수 있는 우선권의 종류
ㄴ static final int MAX_PRIORITY :최대 우선권
ㄴ static final int MIN_PRIORITY : 최소 우선권
ㄴ static final int NORM_PRIORITY: 보통 우선권
* setPriority() 메서드로 우선권을 설정하고 getPriority() 메서드로 현재 스레드의 우선권을 얻어 온다
class Th1 extends Thread {
public void run() {
for(int i=0; i<10000; i++) {
System.out.println("Th1 i: " + i);
}
}
}
class Th2 extends Thread {
public void run() {
for(int i=0; i<10000; i++) {
System.out.println("Th2 i: " + i);
}
}
}
class G extends Thread{
G() {/*
int max = Thread.MAX_PRIORITY;
int min = Thread.MIN_PRIORITY;
int norm = Thread.NORM_PRIORITY;
System.out.print(max + "\n" + min + "\n" + norm);*/
}
void init() {
Th1 th1 = new Th1();
th1.setPriority(Thread.MAX_PRIORITY);
Th2 th2 = new Th2();
th2.setPriority(Thread.MIN_PRIORITY);
th2.yield();
th1.start();
th2.start();
/*
setPriority(Thread.MIN_PRIORITY);
//int pri = getPriority();
//System.out.println("getPriority(): " + pri);
start(); // JVM -> (새)스레드
selfM(); // main() 스레드*/
}
public static void main(String[] args) {
new G().init();
}
}
* 동기화 (Synchronized)
- 공유 메모리를 여러 스레드가 동시에 사용하지 못하도록 하는 것 (lock을 건다)
즉, 하나의 스레드만이 공유 메모리를 참조 할 수 있도록 제한 하는 방법
- synchronized 는 메소드에 사용이 가능한 modifier로서, 여러 스레드에 의해 특정 객체의 메소드들이
동시 호출되는 것에 대해 잠금(lock)을 설정하여,거부하도록 하는 기능을 지원한다.
즉, 코드의 한부분 또는 동기화된 메소드가 다른 스레드와 동시에 수행될 수 없게 하는 것
class H extends Thread{
H() {
m1(); // 메인 스레드
m2();
start(); // 새 스레드
}
public void run() {
m1();
m2();
}
synchronized void m1() {
System.out.println("m1()");
}
void m2() {
System.out.println("1");
{synchronized(this) {
System.out.println("2");
}}
System.out.println("3");
}
public static void main(String[] args) {
new H();
}
}
* 쓰레드 강제 정지 (Thread객체.interrupt())
- 쓰레드가 진행중에 강제로 정지하고자 할때 사용한다
- 플래그 방식과 interrupt()메소드 호출, 이 두가지가 있지만 웬만하면 interrupt() 사용 권장
* 데몬 쓰레드 (Daemon)
- 독립쓰레드(Non Daemon 쓰레드) - 메인쓰레드와 working쓰레드(개발자가 만든 쓰레드)
- 메인쓰레드가 끝나도 종료되지 않고 쓰레드가 Dead상태 될때 까지 계속 실행되는 메소드
- 종속쓰레드(Daemon 쓰레드) - 모든 독립쓰레드가 끝나면 자동으로 종료(Dead)가 되는 쓰레드
- 주쓰레드의 보조역할을 하는 쓰레드
- 종속 쓰레드는 주로 무한루프로 구성한다
ex) 배경음악 깐다든지, 10분마다 자동저장한다든지 등등
* 어떤 쓰레드를 종속쓰레드로 만들려면 setDaemon(true)로 설정
// 종속 쓰레드로 사용할 쓰레드]
// 1] Thread 클래스 상속
class DaemonThread extends Thread{
// 2] run메소드 오버라이딩
@Override
public void run() {
while(true) {
System.out.println(String.format("%s] 배경음악이 흘러요", getName()));
try {
sleep(3000);
System.out.println(String.format("%s] 3초마다 저장", getStackTrace()));
} catch(InterruptedException e) {
e.printStackTrace();
}// try~catch
}// while
}// run()
}// class DaemonThread
public class ThreadApp {
public static void main(String[] args) throws InterruptedException {
System.out.println("[main 쓰레드 시작]");
// 쓰레드로 구현하지 않은 클래스 테스트]
// NotThread nt1 = new NotThread("1st 클래스");
// nt1.notThreadMethod();
// NotThread nt2 = new NotThread("2st 클래스");
// nt2.notThreadMethod();
// 쓰레드로 구현한 클래스 테스트]
YesThread yt1 = new YesThread("1st 쓰레드");
// 메소드로 쓰레드명 설정
yt1.setName("첫번째 쓰레드");// 위 1st 에서 첫번째로 바뀜
yt1.start();// 쓰레드를 Running상태로 전이시킴
// join() 메소드]
// 1] start()호출 후에 join()메소드를 호출해야 한다
// 2] join()메소드를 호출한 쓰레드가 실행이 끝나야 다음 쓰레드가 동작한다
// yt1.join();
YesThread yt2 = new YesThread("2st 쓰레드");
// 쓰레드의 우선권 설정]
// 우선순위가 높다고 그 쓰레드가 먼저 실행된다는 보장은 없다(확률만 높을뿐)
yt2.setPriority(Thread.MAX_PRIORITY);
yt2.start();
// 쓰레드명 미 설정시 자동으로 부여됨]
DaemonThread daemon = new DaemonThread();
daemon.setName("데몬쓰레드");
daemon.setDaemon(true);// 종속쓰레드 설정
daemon.start();// 쓰레드 Runnable 상태로 만든다
System.out.println("현재 활성화 상태(Runnable or Running상태) 있는 쓰레드 수: " + Thread.activeCount());
System.out.println("첫번째 쓰레드의 우선권: " + yt1.getPriority());
System.out.println("두번째 쓰레드의 우선권: " + yt2.getPriority());
System.out.println("현재 실행중인(Running) 쓰레드 명: " + Thread.currentThread().getName());
System.out.println("main 쓰레드의 우선권: " + Thread.currentThread().getPriority());
System.out.println("[main 쓰레드 끝]");
}// main
}// class
예제)
- Runnable
class Command {
void longedMethod() {
for (int i = 0; i <= 10; i++) {
System.out.println(
String.format("[실행중인 쓰레드명] - %s , i = %d",
Thread.currentThread().getName(), i));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} // try~catch
} // for
}// longedMethod()
}// class Command
// 1] Runnable 인터페이스 상속
class Soldier extends Command implements Runnable {
// 2] run() 오버라이딩
@Override
public void run() {
longedMethod();
}// run()
}// class Soldier
public class RunnableApp {
public static void main(String[] args) {
// 1] Runnable 타입을 Thread클래스의 인자 생성자를 이용해서 Thread 타입으로 변환
Soldier soldier = new Soldier();
System.out.println(soldier instanceof Soldier);
System.out.println(soldier instanceof Command);
System.out.println(soldier instanceof Runnable);
// System.out.println(soldier instanceof Thread);[x]
Thread thread1 = new Thread(soldier);
thread1.setName("첫번째 쓰레드");
thread1.start();
Thread thread2 = new Thread(soldier,"두번째 쓰레드");
thread2.start();
}// main
}// class
- synchronized
// 동기화 블락을 이용한 데이타 동기화
// 동기화 블락]
// synchronized(동기화할 객체){
// 동기화 할 로직
// }
class DataSyncClass {
// 여러쓰레드가 공유하는 메모리
int shareData;
// 인자생성자
public DataSyncClass(int shareData) {
this.shareData = shareData;
}// DataSyncClass(int shareData)
}// class DataSyncClass
// 공유 데이터를 사용하는 쓰레드
class DataSyncThread extends Thread {
// [멤버변수]
// 공유할 데이터를 갖고있는 DataSyncClass 타입의 멤버
DataSyncClass dsc;
// 일정하게 증가시킬 숫자를 저장할 멤버
int inc;
// 쓰레드명을 저장할 멤버
String threadName;
// [인자생성자]
public DataSyncThread(DataSyncClass dsc, int inc, String threadName) {
super(threadName);
this.dsc = dsc;
this.inc = inc;
this.threadName = threadName;
}// DataSyncThread(DataSyncClass dsc, int inc, String threadName)
// [오래걸리는 메소드]
// DataSyncClass의 shareData에 저장된 값을 반복하면서 inc(증가분)만큼 누적해서 계속 저장
void increase() {
for (int i = 1; i <= 10; i++) {
dsc.shareData += inc;
System.out.println(
String.format("[쓰레드명: %s, 공유데이터:%d]", getName(), dsc.shareData));
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} // try~catch
} // for
}// increase()
@Override
public void run() {
// 동기화 전]
// increase();
// 동기화 후]
synchronized (dsc) {
increase();
}
}// run()
}// class DataSyncThread extends Thread
public class DataSynchronized {
public static void main(String[] args) throws InterruptedException {
// 공유메모리를 갖고있는 클래스, 하나만 인스턴스화]
DataSyncClass dsc = new DataSyncClass(10);
// 두개의 쓰레드 생성]
DataSyncThread dst1 = new DataSyncThread(dsc, 2, "첫번째 쓰레드");
dst1.start();
// dst1.join(); // 데이터 동기화 없이 특정 쓰레드를 지정해서 동기화 작업을 하고자 할때
DataSyncThread dst2 = new DataSyncThread(dsc, 5, "두번째 쓰레드");
dst2.start();
}// main
}// class
'개발 > JAVA' 카테고리의 다른 글
22. JAVA Network 네트워크 (0) | 2020.06.08 |
---|---|
21. JAVA IO (Input / Output) / 파일 입출력 스트림 / 파일 (File) 클래스 (0) | 2020.06.08 |
19. JAVA 예외(Exception) / try~catch~finally (0) | 2020.06.08 |
18. JAVA 클래스간 형변환 / 업캐스팅 / 다운캐스팅 / 내부클래스 (InnerClass) (0) | 2020.06.08 |
17. JAVA 인터페이스 (Interface) / 컬렉션 (Collection) (0) | 2020.06.08 |