우리가 유투브를 키고(프로세스), MSN메신저(프로세스)키는것이 멀티 프로세스이다.
이처럼 프로세스에서도 멀티 쓰레드를 사용할수 있다.
특히 자바는 언어적 차원에서 스레드를 지원하는 일반적인 언어중 하나로, JVM자체가 하나의 프로세스이기 때문에 멀티스레트 프로그램을 쉽고 명료하게 만들수 있다.
쓰레드는 병렬처리가 가능하기 떄문에, 애플리케이션의 성능과 효율을 상승시킬수 있다.
하지만 CPU사용률이 높은 오랜시간에 걸리는 작업엔 싱글스레드로 처리하는 방식이 효과적이다.
-Thread 인스턴스를 생성
-start를 통해 인스턴스 실행
-Runnable 객체가 run()의 메소드를 실행
-run 메소드의 모든처리가 끝나면 Thread 소멸
쓰레드의 구현 방법중 첫번째 방법은 상속을 이용하는것이다.
Thread 클래스를 상속한뒤 run메소드를 오버라이드 한다.
class ThreadEx1 extends Thread{
public void run(){
//비즈니스 로직
}
}
public class RunnableThreadTest{
Thread t = new ThreadEx1() ;
t.start
}
두번째 방법은 합성을 이용하는 것이다.
class ThreadEx2 implements Runnable {
public void run(){
//비즈니스 로직
}
public class RunnableThreadTest2{
Thread t = new Thread(new RunnableThread());
t.start();
}
상속은 private로 선언되지 않은 모든 변수, 메소드 생성자가 하위 클래스에 노출된다.
이렇게 노출된다는 의미로 white박스라는 의미이다.
상속은 슈퍼클래스가 바뀌면 하위 클래스가 바뀌는 이슈도 생길수 있다.
추가로 기능이 더해질수록, 상속관계가 복잡해져서, 수정과 확장에 손을댈수 없는 상황이 일어날 수 있다.
이처럼 캡슐성과 확장성을 고려하면 , black-box(implements) 이 이롭다.
하지만 합성은 객체간의 간계가 수평이기 때문에 메소드명이 명확하지 않으면 코드의 가독성이 떨어지기 때문에 클래스들을 패키지로 적절하게 분리하고 인터페이스를 잘 설계해야 한다.
stop()은 여러문제점으로 인하여 썬 마이크로 시스템즈에서 메소드를 사용하지 말것을 권고하고, 있고 두가지 구현방법은 통해 구현할수있다.
while(!stoped){
thread.sleep(500);
//지연없이 실행하는것은 cpu에 많은 부담을 준다.
//로직
}
public void stop(){
stopped = false;
}
public class StopThreadTest{
StopThread st = new StopThread();
Thread thread = new Thread(st);
thread.start();
st.stop();
이처럼 플래그를 이용해 할수 있으나, 특정로직을 무한루프를 돌수있는 문제가 생길수도 있다.
class ThreadEx2 implements Runnable {
public void run(){
while(!Thread.currentThread().isInterrupted()){
}catch(InterruptedException e){
}finally{
//마무리 해야할것
인터럽트를 이용하여 스레드를 즉각 종료시키고 안전하게 마무리 작업(DB커넥션, 소켓 종료)
을 할 수 있다.
자바는 어플리케이션 내부의 모든 스레드가 종료되지 않으면 JVM이 종료되지않는다.
그래서 만약 백그라운드 작업이 일반스레드로 설정되어있다면, 영원히 어플리케이션은 정지되지 않는다.
그래서 자바에서는 데몬스레드라는 개념을 도입했다.
class ThreadEx2 implements Runnable {
public void run(){
//비즈니스 로직
}
public class RunnableThreadTest2{
Thread t = new Thread(new RunnableThread());
t.setDaemon(true);
t.start();
//메인의 실행이 끝나면 쓰레드는 죽는다.
}
main 스레드가 생성해서 실행시킨 스레드가 종료될까지 main 스레드가 기다려야하는 상황이라면 어떻게 할까?
그럴때 join()이라는 함수를 이용한다.
A , B라는 스레드가 있을때, join을 사용하면 A스레드가 B스레드를 시작시켰을때 B스레드가 끝날때까지 기다린후 자신의 나머지 작업을 계속 진행시킨다.
스레드를 여러개 운영하는 어플리케이션이라면 특정 스레드가 먼저 실행되게 하고 싶은 경우가 있을것이다.
A라는 인스턴스에 여러개의 스레드가 접근하려 할때, 한차례에 한개씩 밖에 접근하지못하기 때문이다.
Thread 클래스에서는 개발자가 스레드의 우선순위를 설정할수있다.
Thread.setPriority() // 1~10까지 안에 숫자를 넣는다. 기본적으로 값이 5 이다.
하지만 스레드 우선순위는 높은수준의 지식없이는 버그가 발생할 가능성이 높기 때문에 가급적 사용하지 말것을 권장한다.
만약 두개의 스레드가 동시에 같은 값을 바꾸면 어떻게 될까?
1000원의 잔고가 있고, A 쓰레드가 500원을 출금하는 동시에 B쓰레드가 1000원을 출금한다면,
아직 잔고에는 차감되지 않았기때문에 1500원이 출금되어버린다. 그만큼 동기화를 어떻게 해결해야 할까?
어떤객체에 여러스레드가 접근할때 동시에 접근하지 못하도록 하기위한것으로, Heap에 lock이 자동생성된다 이렇게 생성된 lock은 sychronized 키워드를 통해 사용한다.
public class SynchronizedTest{
public synchronized String drawOutString(String money money)
throws AccountException{
// 잔액을 계산하여 크면 정상처리하고, 반대일 경우 AccountException을 처리한다.
}
}
public class SynchronizedTest{
public String drawOutString(String money money)
throws AccountException{
synchronized(userAccount){
}
}
}
sychronized를 사용하게 되면
일반 변수의 수명은 코드블록 범위내에서만 유효하지만,ThreadLocal은 쓰레드 영역에 변수를 설정할수 있어서 쓰레드가 실행하는 모든영역에서 변수를 사용할 수 있게 된다.
//이후에 계속