run
이라는 싱글 메소드를 정의한다. public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
}
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
public class HelloThread extends Thread {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
출처 : 자바의 정석
start() 는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생성한 다음에 run()을 호출해서 생성된 호출 스택에 run()이 첫번째로 올라가게 한다.
모든 쓰레드는 독립적인 작업 수행 위해 자신만의 호출 스택 필요하다.
새로운 쓰레드 생성, 실행할 때마다 새로운 호출 스택 생성되고 쓰레드 종료시 해당 호출스택 소멸
왼쪽의 start메서드가 오른쪽 쪽처럼 새로운 호출스택을 생성하고 새로 생성된 호출스택에서 run()메서드가 실행된다.
start()를 호출했다고 해서 바로 해당 쓰레드를 실행하는 것은 아니다. 일단 실행대기 상태에 있다가 자신 차례가 되어야 실행.
한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다. 하나의 쓰레드에 대해 strat()는 한 번만 호출
// 코드스쿼드 호눅스 코드
public class ThreadAlreadyExist extends Thread {
@Override
public void run() {
this.setName("newThread");
System.out.printf("%s %d start%n", this.getName(), this.getId());
}
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.printf("Main Thread : %s %d start%n", t.getName(), t.getId());
Thread newThread = new ThreadAlreadyExist();
newThread.start();
System.out.println("Main Thread end");
}
}
상태 | 설명 |
---|---|
NEW | 쓰레드가 생성된 후 아직 start() 메서드가 호출되지 않은 상태, 실행상태로 갈 수 없음 |
RUNNABLE | 실행 중 또는 실행가능 상태 (ready + running) |
BLOCKED | 동기화 블럭에 의해서 일시 정지된 상태 (lock이 풀릴 때까지 기다리는 상태 |
WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnable) 일시정지상태 |
TIMED_WAITING | 일시정지 시간이 지정된 경우 |
TERMINATED | 쓰레드의 작업이 종료된 상태 |
Thread.sleep
은 현재 쓰레드의 실행을 일정기간동안 중지시킨다. static void sleep(long millis)
static void sleep(long millis, int nanos)
try {
Thread.sleep(1, 500000);
} catch(InterruptedExecution e) {
}
TIMED_WAITING
public class ThreadDefinitionStart {
public static void main(String[] args) {
Runnable r = new HelloRunnable();
Thread t = new Thread(r);
t.start();
Thread.sleep(1000); //main 메서드 1초쉬기. HelloRunnable의 sleep메서드 동작되도록 기다리기
System.out.println(t.getState()); // 출력값 TIMED_WAITING
}
}
class HelloRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(40000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello Runnable from a thread!");
}
}
t.sleep()
) 아닌 Thread.sleep()
을 사용한다.public class WhichSleep {
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread2 t2 = new Thread2();
t1.start();
try {
t1.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
System.out.println("Main종료");
}
}
class Thread1 extends Thread {
public void run() {
for (int i = 0; i < 300; i++) {
System.out.println("-");
}
System.out.println("th1종료");
}
}
class Thread2 extends Thread {
public void run() {
for (int i = 0; i < 300; i++) {
System.out.println("|");
}
System.out.println("th2종료");
}
}
InterruptedException
InterruptedException
이 예외를 던지기 때문에 try-catch문으로 감싸주거나 메서드에 throws InterruptedException
해줘야 한다. InterruptedException
이 던진 예외를 처리해준 코드 예 // 코드스쿼드 수업 호눅스 코드
package bongf.week10.study;
public class SleepThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.err.println("왜깨우시죠");
}
System.out.println("Thread wake");
}
public static void main(String[] args) throws InterruptedException {
System.out.println("Main start");
Thread t = new Thread(new SleepThread());
t.start();
Thread.sleep(1000);
t.interrupt(); // 이 코드 없다면 "왜깨우시죠" 는 출력 안된다
System.out.println("Main end");
}
}
//자바의 정석 코드
void delay(long millis) {
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
//코드스쿼드 수업 호눅스 코드
public class SleepThread extends Thread {
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread t = new SleepThread();
t.start();
Thread.sleep(2000);
t.interrupt();
System.out.println("end");
}
public void run() {
long count = 0;
while(!isInterrupted()) {
count++;
}
System.out.println("Thread wake : " + count);
}
interrupted()
는 interrupt()가 호출되었는지 확인하는 것은 isInterrupted()
와 똑같지만 쓰레드의 interrupted 상태를 false로 초기화 시킨다는 점에서 차이가 있다. InterruptedException
예외를 던져 종료되는 메서드는 interrupt 상태를 초기화시킨다(false). false되자마자 다른 쓰레드에 의해 다시 interrupt되어 true로 되는 것은 가능 t.join() // 쓰레드 t가 종료될 때까지 기다린다.
void join()
void join(long millis)
void join(long millis, int nanos)
WAITING
package bongf.week10.study;
public class JoinThread {
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread3 t3 = new Thread3();
t3.start();
Thread.sleep(1000);
System.out.println(t3.getState()); // WAITING
}
}
class Thread3 extends Thread {
public void run() {
System.out.println("t3 start");
Thread4 t4 = new Thread4();
t4.start();
try {
t4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread3 ends");
}
}
class Thread4 extends Thread {
public void run() {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread4 ends");
}
}
void setPriority(int newPriority) // 우선순위 지정
int getPriority() // 우선순위 반환
public class MainThread {
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.println(t.getName()); // main
System.out.println(t.getPriority()); // 5
}
}
사용자 쓰레드(user thread)
와 데몬 쓰레드(demon thread)
두 종류가 있다. public class Daemon implements Runnable{
@Override
public void run() {
while(true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {}
if( ) // 특정조건 {
}
}
}
//코드스쿼드 호눅스 수업 참고
public class Daemon extends Thread{
@Override
public void run() {
this.setName("Daemon");
System.out.printf("%s starts%n", this.getName());
int count = 0;
while(true) {
try {
Thread.sleep(1000);
count++;
System.out.printf("Dameon %d초 진행 중%n", count);
} catch (InterruptedException e) {}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = Thread.currentThread();
System.out.printf("%s starts%n", t.getName());
Thread d = new Daemon();
d.setDaemon(true);
d.start();
Thread.sleep(2000);
System.out.printf("%s ends%n", t.getName());
}
}
public class Daemon extends Thread{
@Override
public void run() {
this.setName("Daemon");
System.out.printf("%s starts%n", this.getName());
Thread childDaemon = new Daemon();
childDaemon.setName("childDaemon");
System.out.printf("제 이름은 %s 입니다 . 제가 데몬일까요? %s %n", childDaemon.getName(), childDaemon.isDaemon());
int count = 0;
while(true) {
try {
Thread.sleep(1000);
count++;
System.out.printf("%s %d초 진행 중%n", this.getName(), count);
} catch (InterruptedException e) {}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = Thread.currentThread();
System.out.printf("%s starts%n", t.getName());
Thread d = new Daemon();
d.setDaemon(true);
d.start();
Thread.sleep(2000);
System.out.printf("%s ends%n", t.getName());
}
}
happens-before
관계를 구축한다. 임계영역(critical section)
과 잠금(락, lock)
개념 도입 쓰레드 동기화(synchronization)
이라고 한다. synchronized블럭
통해 쓰레드 동기화 지원했지만 JDK1.5부터는 java.util.concurrent.locks
와 java.util.concurrent.atomic
패키지 통해 다양한 방식으로 지원 thread contention
(쓰레드 경합)을 발생시킬 수 있다.
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
- c 초깃값 = 0
- Thread A : c 정보 조회 (0)
- Thread B : c 정보 조회 (0)
- Thread A : 조회된 값을 증가시킨다 (1)
- Thread B : 조회된 값을 감소시킨다 (-1)
- Thread A : 결과를 c에 저장 (1)
- Thread B : 결과를 c에 저장 (-1)
int counter = 0;
counter++; // Thread A
System.out.println(counter); // Thread B
happens-before
관계를 맺어줘야 한다. happens-before
관계를 맺어주는 방법은 여러가지가 있는데 그 중 하나가 synchronization
이다. Synchronized Methods
Synchronized Methods
동기화된 메서드 : 메서드 전체를 임계 영역으로 설정public synchronized void calcSum() {
}
synchronized (참조변수)
를 붙이는 것 public void method() {
synchronized(객체의 참조변수) {
}
}
synchronized
키워드를 메서드 선언부에 더하면 된다. public class SynchronizedCounter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
happens-before
관계를 설정한다. 이는 객체 상태의 변경이 모든 쓰레드에게 보여지게 한다. final
fields에는 동기화 블럭 사용할 필요가 없다. final 이니까 public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
public class Deadlock {
public static void main(String[] args) {
Friend alphonse = new Friend("Alphonse");
Friend gaston = new Friend("Gaston");
new Thread(new Runnable() {
@Override
public void run() {
alphonse.bow(gaston);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
gaston.bow(alphonse);
}
}).start();
}
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return name;
}
public synchronized void bow(Friend bower) {
System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName());
}
}
}