passion and relax

[JAVA] 15. 연결하는 방법 (네트워크 소켓과 멀티스레딩) 본문

프로그래밍

[JAVA] 15. 연결하는 방법 (네트워크 소켓과 멀티스레딩)

Grab Java 2024. 6. 4. 10:50

간단한 서버-클라이언트 소켓 통신 예

server
public class DailyAdviceServer {
    String[] adviceList = {"aaa", "bbb", "ccc"};

    public void go() {
        try {
            ServerSocket serverSock = new ServerSocket(4242);
           
            while(true) {
                Socket sock = serverSock.accept();
                PrintWriter writer = new PrintWriter(sock.getOutputStream());
                writer.println(adviceList[(int) (Math.random() * adviceList.length)]);
                writer.close();
            }
        } catch (IOException ex) {
            Logger.getLogger(DailyAdviceServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
   
    public static void main(String[] args) {
        DailyAdviceServer server = new DailyAdviceServer();
        server.go();
    }
}

 

client
public class DailyAdviceClient {

    public void go() {
        try {
            Socket s = new Socket("localhost", 4242);
            InputStreamReader sReader = new InputStreamReader(s.getInputStream());
            BufferedReader r = new BufferedReader(sReader);
            String advice = r.readLine();
            System.out.println(advice);
            r.close();
        } catch (Exception ex) {
            Logger.getLogger(DailyAdviceClient.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
   
    public static void main(String[] args) {
        DailyAdviceClient client = new DailyAdviceClient();
        client.go();
    }
}

 


간단한 스레드 예

public class MyRunnable implements Runnable {

    @Override
    public void run() { go(); }

    public void go() { doMore(); }

    public void doMore() { System.out.println("top o' the stack"); }
}

class ThreadTester {

    public static void main(String[] args) {
        Runnable threadJob = new MyRunnable();
        Thread myThread = new Thread(threadJob);
        myThread.setName("HaHa");  //스레드 기본 이름 무시하고, 새 이름 부여
        myThread.start();
       
        System.out.println("back in main");
    }
}

 


스레드의 상태

새로운 스레드
    Thread t = new Thread(r);
실행 가능한 상태 (실행 중은 아님)
    t.start();
실행 중인 상태
    스레드 스케줄러에 의해, 이 스레드가 선택되었을 때 실행된다.
다시 실행 가능한 상태
    실행 중인 상태에서 다른 스레드에 기회를 주기 위해 실행 가능한 상태로 다시 빠짐
봉쇄 상태 (일시적인 실행 불가능 상태)
    스트림으로 부터 들어오는 데이터를 기다리고 있을 때
    대기 상태로 들어 갔을 대
    객체에 대한 잠금이 해제되기를 기다리고 있을 때

 


Thread.sleep()

실행 중인 상태에서 실행 가능한 상태로 강제 전환
실행 가능한 상태로 갔다가 다시 스레드 스케줄러에 의해 선택되어질 때까지 실행되지 않는다.
즉, 설정한 sleep() 시간 보다 더 걸리게 되어, 완벽한 타이밍을 확신할 수는 없다.

 

try {
    Thread.sleep(2000);   //2000밀리초 == 2초
} catch (InterruptedException ex) {
    ex.printStackTrace();
}

 


스레드 병행성 (concurrency) 문제 : 객체의 락을 이용하여 해결

모든 자바 객체에는 자물쇠(락)가 하나씩 있다.
대부분 이 자물쇠는 열려 있으나, 멀티 스레드 환경에서 synchronized 메소드가 정의 되어 있다면 의미를 갖는다.
여러 스레드 중, 어느 하나가 synchronized 메소드를 만나면, 이 유일한 락을 가진 하나의 스레드만이 메소드 안에 들어갈 수 있다.


synchronized 방법 1 : 함수 전체를 동기화 하는 방법

public synchronized void increment() {
    int i = balance;   //balance 멤버 변수를 i로 대입
    ~~
    //여기서 sleep 해도, synchronized 이기에 안전하다.
    ~~
    balance = i + 1;
}

 

synchronized 방법 2 : 필요한 곳만 동기화 하는 방법

public void increment() {
    synchronized(this) {
        int i = balance;
        ~~
        balance = i + 1;
    }
}

 


스레드 교착상태(dead lock) 문제

두 스레드가 서로 상대의 락을 요구하며, 계속 기다리는 상태
    A 스레드 : foo 락 (실행 가능 상태)
    B 스레드 : bar 락 (실행 상태)
    B 스레드 : bar 락 + foo 락 요구 (봉쇄 상태)
    A 스레드 : foo 락 (실행 상태)
    A 스레드 : foo 락 + bar 락 요구 (봉쇄 상태)
DB는 설정 시간 동안 반응이 없으면 트랜잭션 롤백을 해서 해결하지만, 자바는 처리 메커니즘이 없다.