Multi Thread: 2개 이상의 스레드가 수행중인 상황
대부분의 경우
1) Priority(우선 순위): 누가 먼저, 자주
I/O(파일 입출력, 네트워크 입출력, 화면 출력): 시간이 오래 걸리는 작업
연산: 시간이 짧게 걸리는 작업
2) Thread Group: 여러 개의 스레드 관리
Semaphore
3) Mutual Exclusion(상호 배제)
-> 하나의 스레드가 사용 중인 공유 자원은 다른 스레드가 수정 할 수 없음
-> Lock과 Synchronized(순서대로)로 해결 <-> Asynchronized(순서를 알 수 없음)
4) 생산자와 소비자 문제
-> wait와 notification으로 해결
5) DeadLock
-> 결코 발생할 수 없는 사건을 무한정 기다리는 것
8. Thread Group
-> 관련된 스레드들을 하나의 그룹으로 묶어서 사용하기 위한 개념
-> ThreadGroup 이라는 클래스를 제공하지만 몇 몇 메서드가 제대로 동작하지 않는 문제 때문에 대부분의 경우 배열이나 List를 이용해서 구현하는 것을 권장
9. Thread의 종료
-> run 메서드 종료
-> 스레드의 Interrupt 메서드를 호출하고 스레드의 run 메서드 안에서 InterruptedException이 발생하면 run 메서드를 종료하도록 만들어서 강제 종료하도록 할 수 있음
-> Daemon Thread는 Daemon이 아닌 다른 스레드가 존재하지 않으면 자동으로 종료
-> Thread의 실행 제어 메서드
void join(long millis, int nanos): 지정된 시간 동안 스레드를 수행하고 다른 스레드에게 제어권을 넘기는 메서드
void suspend( ): 스레드를 일시 정지 시키는 메서드로 resume 메서드로 다시 시작
void resume( ): suspend된 스레드를 다시 시작
void yield( ): 다른 스레드에게 제어권을 넘겨 주는 메서드
10. Mutual Exclusion(상호 배제)
-> 하나의 스레드가 사용 중인 공유 자원을 다른 스레드가 수정하면 안됨
1) 공유 자원을 여러 개의 스레드가 동시에 사용했을 때 문제
-> 공유 자원으로 사용할 데이터 클래스
=> 실습
package naver.srlee3637.multithread;
//자원을 가지고 연산을 하는 스레드에 사용할 클래스
public class ShareData implements Runnable {
//연산 결과를 저장할 속성
private int result;
//연산에 사용할 인덱스
private int idx;
//result의 getter메서드
public int getResult(){
return result;
}
@Override
public void run() {
try {
for(int i = 0; i < 5; i++) {
idx++;
Thread.sleep(10);
result = result + idx;
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
package naver.srlee3637.multithread;
public class MutexMain {
public static void main(String[] args) {
//Runnable 인터페이스로 부터 상속 받은 클래스
ShareData shareData = new ShareData();
//스레드 생성
Thread th1 = new Thread(shareData);
th1.start();
Thread th2 = new Thread(shareData);
th2.start();
try {
//30초 대기 - 앞의 작업이 스레드로 동작하기 때문에 작업이 끝날 때 까지 대기하고 결과 출력
Thread.sleep(3000);
System.out.println(shareData.getResult());
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
--> 실행을 하면 이상한 결과가 출력됨
ShareDate 인스턴스를 1개만 만들어서 공유하는데 하나의 작업이 완료되기 전에 다른 스레드가 공유 자원을 수정하기 때문에 발생
2) 해결 방법
-> 한번에 실행되어야 하는 코드를 가진 영역을 찾고 메서드에 synchronized를 붙이 던가 코드 영역을 synchronized(공유객체){ } 로 묶어 주면 됨
이렇게 묶어 주면 묶인 영역의 코드는 동시에 수정할 수 없음
되도록이면 synchronize 메서드를 만드는 것 보다는 블럭을 만드는 것을 권장
메서드를 묶게되면 영역이 커져서 공유도가 떨어지기 때문
3)ShareData 수정해서 다시
=> 실습
package naver.srlee3637.multithread;
//자원을 가지고 연산을 하는 스레드에 사용할 클래스
public class ShareData implements Runnable {
//연산 결과를 저장할 속성
private int result;
//연산에 사용할 인덱스
private int idx;
//result의 getter메서드
public int getResult(){
return result;
}
@Override
public void run() {
try {
for(int i = 0; i < 5; i++) {
synchronized(this){
idx++;
Thread.sleep(10);
result = result + idx;
}
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
4) 최근에는 위의 방법 말고 ReentrantLock이라는 클래스를 이용하는 것을 권장
-> 인스턴스를 생성하고 lock이라는 메서드로 공유 영역을 만들고 unlock이라는 메서드로 공유 영역을 해제
=> 실습
package naver.srlee3637.multithread;
import java.util.concurrent.locks.ReentrantLock;
//자원을 가지고 연산을 하는 스레드에 사용할 클래스
public class ShareData implements Runnable {
//연산 결과를 저장할 속성
private int result;
//연산에 사용할 인덱스
private int idx;
//공유 코드 영역을 설정하기 위한 객체
static final ReentrantLock lock = new ReentrantLock();
//result의 getter메서드
public int getResult(){
return result;
}
@Override
public void run() {
try {
for(int i = 0; i < 10; i++) {
//자물쇠를 채워서 unlock을 만날 때까지는 이 영역의 자원을 수정할 수 없음
lock.lock();
idx++;
Thread.sleep(10);
result = result + idx;
lock.unlock();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
11. 생산자와 소비자 문제
-> 생산자와 소비자는 동시에 수행할 수 있는데 소비자는 생산자가 자원을 생성해준 경우에만 동작을 해야함
소비자가 자원이 생성되지 않았는데 작업을 수행하면 예외 발생
1)생산자와 소비자 문제 발생
-> 공유 자원의 역할을 수행할 클래스 - Product
=> 실습
package naver.srlee3637.multithread;
import java.util.ArrayList;
import java.util.List;
//공유 자원의 역할을 수행할 클래스 - 진열대 역할
public class Product {
//문자를 저장할 수 있는 List - 공유 자원
List<Character> list;
//생성자
public Product() {
list = new ArrayList<>();
}
//생산자 메서드
public void put(Character ch) {
list.add(ch);
System.out.println("창고에 제품 " + ch + "가 입고 되었습니다.");
try {
Thread.sleep(1000);
System.out.println("재고 수량:" + list.size());
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
//소비자 메서드
public void get() {
//첫번째 데이터를 꺼내서 ch에 대입
Character ch = list.remove(0);
System.out.println("창고에서 제품 " + ch + "를 출고 하였습니다.");
try {
Thread.sleep(1000);
System.out.println("재고 수량:" + list.size());
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
-> 생산자 스레드 클래스: Producer
=> 실습
package naver.srlee3637.multithread;
public class Producer extends Thread {
//공유자원 속성
private Product myList;
//생성자
public Producer(Product myList) {
this.myList = myList;
}
@Override
public void run() {
//삽입 작업을 26번 수행
for(char ch = 'A'; ch <= 'Z'; ch++) {
myList.put(ch);
}
}
}
-> 소비자 스레드 클래스: Customer
=> 실습
package naver.srlee3637.multithread;
public class Customer extends Thread {
private Product myList;
//생성자
public Customer(Product myList) {
this.myList = myList;
}
@Override
public void run() {
for(int i = 0; i < 26; i++) {
myList.get();
}
}
}
-> main메서드를 소유한 클래스를 만들어서 스레드 2개를 실행 : ProducerConsumerMain
=> 실습
package naver.srlee3637.multithread;
public class ProducerConsumerMain {
public static void main(String[] args) {
//자원생성
Product product = new Product();
//하나의 자원을 이용해서 2개의 스레드를 생성해서 실행
new Producer(product).start();
new Customer(product).start();
}
}
-> 실행을 하다보면 예외 발생
소비자 스레드인 Customer 스레드가 리스트에 아무런 데이터가 없는데 꺼낼려고 해서 발생하는 문제
소비자 스레드의 메서드는 데이터가 없을 대는 대기하고 생산자 스레드는 데이터를 만들어 낸 경우 데이터가 만들어 졌다고 알려줘야함
기다리기 위해서 호출하는 메서드는 wait이고 알려주는 메서드는 notify 와 notifyAll이라는 메서드
이 메서드들은 Object클래스의 메서드 이고, Synchronze 메서드 안에서만 동작
-> product클래스를 수정하고 실행
=> 실습
package naver.srlee3637.multithread;
import java.util.ArrayList;
import java.util.List;
//공유 자원의 역할을 수행할 클래스 - 진열대 역할
public class Product {
//문자를 저장할 수 있는 List - 공유 자원
List<Character> list;
//생성자
public Product() {
list = new ArrayList<>();
}
//생산자 메서드
public synchronized void put(Character ch) {
list.add(ch);
System.out.println("창고에 제품 " + ch + "가 입고 되었습니다.");
try {
Thread.sleep(1000);
System.out.println("재고 수량:" + list.size());
} catch (Exception e) {}
//물건을 생산했다고 알려줌
notify();
}
//소비자 메서드
public synchronized void get() {
try {
if(list.size() == 0) {
wait();
}
} catch (Exception e) {}
//첫번째 데이터를 꺼내서 ch에 대입
Character ch = list.remove(0);
System.out.println("창고에서 제품 " + ch + "를 출고 하였습니다.");
try {
Thread.sleep(1000);
System.out.println("재고 수량:" + list.size());
} catch (Exception e) {}
//물건을 생산했고 알려줌
}
}
-> 다시 실행해 보면 예외가 발생하지 않음
12. Dead Lock
-> 결코 발생할 수 없는 사건을 무한정 기다리는 것
-> Synchronize 블럭이 여러 개 있는 경우 멀티 스레드를 사용하면 발생
13. Semaphore
-> 동시에 사용할 수 있는 Thread의 개수를 제한
-> Semaphore 클래스를 이용해서 설정
-> 메서드
acquire( ): 리소스를 확보하는 메서드
release( ): 리소스를 해제하는 메서드
1)세마포어를 이용하지 않는 경우 - 스레드가 동시에 전부 실행
-> 스레드 클래스 : SemaphoreThread
=> 실습
package naver.srlee3637.multithread;
public class SemaphoreThread implements Runnable{
String message;
public SemaphoreThread(String message) {
this.message = message;
}
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println(message);
} catch (Exception e) {}
}
}
-> main 메서드를 소유한 클래스를 만들어서 실행: SemaphoreMain
=> 실습
package naver.srlee3637.multithread;
public class SemaphoreMain {
public static void main(String[] args) {
Thread th1 = new Thread(new SemaphoreThread("카리나"));
Thread th2 = new Thread(new SemaphoreThread("지젤"));
Thread th3 = new Thread(new SemaphoreThread("윈터"));
Thread th4 = new Thread(new SemaphoreThread("닝닝"));
th1.start();
th2.start();
th3.start();
th4.start();
}
}
-> 4개의 Thread가 10초 동시에 메세지를 출력
2) 세마포어를 이용하도록 수정
-> 스레드 클래스 수정
=> 실습
package naver.srlee3637.multithread;
import java.util.concurrent.Semaphore;
public class SemaphoreThread implements Runnable{
String message;
Semaphore semaphore;
public SemaphoreThread(String message, Semaphore semaphore) {
this.message = message;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
//리소스 확보
semaphore.acquire();
Thread.sleep(10000);
System.out.println(message);
} catch (Exception e) {}
//리소스 해제
semaphore.release();
}
}
-> 메인 클래스 수정
=> 실습
package naver.srlee3637.multithread;
import java.util.concurrent.Semaphore;
public class SemaphoreMain {
public static void main(String[] args) {
//2개씩 실행할 수 있는 세마포어
Semaphore semaphore = new Semaphore(2);
Thread th1 = new Thread(new SemaphoreThread("카리나", semaphore));
Thread th2 = new Thread(new SemaphoreThread("지젤", semaphore));
Thread th3 = new Thread(new SemaphoreThread("윈터", semaphore));
Thread th4 = new Thread(new SemaphoreThread("닝닝", semaphore));
th1.start();
th2.start();
th3.start();
th4.start();
}
}
**정규 표현식
1. 개요
-> 문자열의 집합을 표현하는데 사용하는 형식 언어
-> Perl에서 처음 사용
-> 많은 프로그래밍 언어가 정규 표현식 기능을 제공 하고 있는데 언어 자체가 제공하기도 하고 별도의 라이브러리 형태로 제공되기도 함
-> 문자열 패턴을 검사하기 위해서 사용
유효성 검사나 자연어 처리에서 많이 사용
-> java에서는 java.util.regex 패키지에 있는 Match 클래스와 Pattern 클래스 그리고 String 클래스에서 제공
-> Web Programming에서는 Java보다는 Javascript에서 많이 사용
2. Network - java.net
-> 다른 컴퓨터와 통신을 하기 위해서는 다른 컴퓨터의 프로그램이나 자원에 접근하기 위한 주소를 알아야함
프로그램에 접근할 때는 IP(컴퓨터를 구분하기 위한 주소)와 포트(프로세스를 구분하기 위한)번호 또는 Domian을 알아야함
1)통신 절차
-> 요청을 처리하는 쪽에서 포트번호를 가지고 소켓을 생성해서 시작
-> 요청을 보내는쪽에서 요청을 처리하는 쪽의 구별하기 위한 주소를 가지고 접속을 하고 요청을 전송
-> 요청을 처리하는 쪽에서 요청을 보내는 쪽의 데이터(parameter)를 읽어서(여기까지가 Request)
처리를 수행한 후 처리 결과를 요청을 보내는쪽으로 전송
-> 요청을 보내는 것을 Request라고 하고 요청을 처리해서 결과를 전송하는 것을 Response라고 함
2) TCP 형태로 클라이언트와 서버가 대화를 주고 받는 것(클라이언트가 서버에게 전송하면 서버가 다시 응답하는 것
TCPServer 작성
=> 실습
package naver.srlee3637.network;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) {
//try ~ resources 구문으로 메모리 해제를 하지 않아도 됨
try(ServerSocket ss = new ServerSocket(9999);) {
System.out.println("서버 대기 중...");
//클라이언트의 요청 대기
try {
Socket socket = null;
while(true) {
//클라이언트의 요청을 대기하다가 클라이언트의 요청이 오면 접속
socket = ss.accept();
//접속자 정보 출력
System.out.println("접속자 정보: " + socket.toString());
//접속자와 문자열을 읽을 수 있는 스트림 생성
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//한줄의 메세지 읽기
String message = br.readLine();
System.out.println(message);
//메세지를 보낼 수 잇는 스트림을 생성
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println("서버가 보내는 메세지");
pw.flush();
//정리 작업
pw.close();
br.close();
socket.close();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
} finally {
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
TCPClient 작성: 자기 컴퓨터에 전송할 때는 127.0.0.1이나 localhost라고 하면 됨
외부의 컴퓨터에서 내 컴퓨터로 데이터가 전송이 가능하도록 할 때는 방화벽을 해제해 주어야 함
=> 실습
package naver.srlee3637.network;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) {
try( Scanner sc = new Scanner(System.in);) {
while(true) {
Socket socket = new Socket(InetAddress.getByName("172.30.1.97"),9999);
System.out.print("보낼 메세지:");
String message = sc.nextLine();
//메시지 보내기
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
pw.println(message + "\n");
pw.flush();
//메시지 받기
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = br.readLine();
System.out.println(response);
br.close();
pw.close();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
'JAVA' 카테고리의 다른 글
30일차 22.11.02 JSON파일 읽어오기 (0) | 2022.11.02 |
---|---|
27일차 22.10.28 (0) | 2022.10.28 |
26일차 22.10.27 (0) | 2022.10.27 |
25일차 22.10.26 (0) | 2022.10.26 |
24일차 22.10.25 (0) | 2022.10.25 |
댓글