Multi-Thread
- 2개 이상의 스레드가 수행 중인 상황 대부분의 경우
<multi-thread 사용시 고려할 것>
- Priority(우선순의) : 누가 먼저, 자주
- Thread Group : 여러개의 스레드 관리
- Mutual Exclusion(상호배제)(뮤텍스)
- 하나의 스레드가 사용중인 공유 자원을 다른 스레드가 수정할 수 없다.
- Lock과 Synchronized(순서대로 처리)로 해결
- 생산자와 소비자 문제
- wait과 notification으로 해결
- Dead Lock
- 결코 발생할 수 없는 상황을 무한정 기다리는 것.
Thread Group
- 관련된 스레드들을 하나의 그룹으로 묶어서 사용하기 위한 개념
- ThreadGroup이라는 클래스를 제공하지만 몇몇 메서드가 제대로 동작하지 않는 문제때문에 대부분의 경우 배열이나 List를 이용해서 구현하는 것을 권장
Thread의 종료
- run method의 종료
- thread의 interrypt method를 호출하고 thread의 run method 안에서 interryptedException이 발생하면 run method를 종료하도록 만들어 강제 종료하도록 할 수 있다.
- Deamon Thread는 Daemon이 아닌 다른 스레드가 존재하지 않으면 자동으로 종료됩니다.
- 스레드의 실행 제어 메서드
- void join(long milis, int nanos) : 지정된 시간동안 스레드를 수행하고 다른 스레드에게 제어권을 넘기는 method
- void suspend() : 스레드를 일시 정지 시키는 메서드로 resume 메서드로 다시 시작 할 수 있다.
- void resume() : suspend된 스레드를 다시 시작
- void yield() : 다른 스레드에게 제어권을 넘겨주는 메서드
Mutual Exclusion(상호배제)
- 하나의 스레드가 사용 중인 공유 자원을 다른 스레드가 수정하면 안됨
1) 공유 자원을 여러개의 스레드가 동시에 사용했을 때 문제
- 공유 자원으로 사용할 데이터 클래스
package com.eb1031.multithread;
import java.util.concurrent.locks.ReentrantLock;
//자원을 가지고 연산을 하는 스레드에 사용할 클래스
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< 10; i++) {
idx++;
Thread.sleep(1);
result = result + idx;
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
package com.eb1031.multithread;
import java.util.concurrent.locks.ReentrantLock;
public class MutexMain {
public static void main(String[] args) {
//Runnable 인터페이스로부터 상속받은 클래스
ShareData sd = new ShareData();
//스레드 생성
Thread th1 = new Thread(sd);
th1.start();
Thread th2 = new Thread(sd);
th2.start();
try {
//30초 대기 - 앞의 작업이 스레드로 동작하기 때문에 작업이 끝날때까지 대기하고 결과를 출력
Thread.sleep(200);
System.out.println(sd.getResult());
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
-> syncronized 없이 실행한 경우 이상한 결과가 출력된다.
: shareData 인스턴스를 개만 만들어서 공유하는데 하나의 작업이 완료되기 전에 다른 스레드가 공유 자원을 수정하기 때문에 문제가 발생한다.
2) 해결방법
- 한번에 실행되어야 하는 코드를 가진 영역을 찾고 메서드에 synchronized를 붙이던가 코드 영역을 synchronized(공유 객체){}로 묶어주면 된다.
- 이렇게 묶어주면 묶인 영역의 코드는 동시에 수정할 수 없다. 되도록이면 synchronized메서드를 만드는 것 보다는 블럭을 만드는 것을 권장한다.
- 메서드를 묶게 되면 영역이 커져서 공유도가 떨어지기 때문이다.
@Override
public void run() {
try {
for(int i = 0; i< 10; i++) {
synchronized (this) {
/* 다른애가 못쓰도록 하는 것은 최소화 해야됨. (화장실 문을 잠그는게 아니라 칸 문만 잠그면 됨) */
idx++;
Thread.sleep(1);
result = result + idx;
}
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
* 최근에는 위의 방법 말고 ReentrantLock이라는 클래스를 이용하는 것을 권장한다.
:인스턴스를 생성하고 lock이라는 메서드로 공유 영역을 만들고 unlock이라는 메서드로 공유 영역을 해제 한다.
//공유 코드 영역을 설정하기 위한 객체
static final ReentrantLock lock = new ReentrantLock();
public void run() {
try {
for(int i = 0; i< 10; i++) {
//자물쇠를 채워서 unlock을 만날때 까지는 이 영역의 자원을 수정할 수 없다.
lock.lock();
idx++;
Thread.sleep(1);
result = result + idx;
lock.unlock();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
생산자와 소비자 문제
- 생산자와 소비자는 동시에 수행할 수 있는데 소비자는 생산자가 자원을 생성해준 경우에만 동작을 해야 한다.
- 소비자가 자원이 생성되지 않았는데 작업을 수행하면 예외가 발생한다.
1)생산자와 소비자 문제 발생
- 공유자원의 역할을 수행할 클래스 -> Product
package com.eb1031.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());
}
}
}
- 생산자 Thread Producer
package com.eb1031.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);
}
}
public Producer() {
// TODO Auto-generated constructor stub
}
}
- 소비자 Thread Customer
package com.eb1031.multithread;
public class Customer extends Thread{
public Product myList;
public Customer(Product myList) {
this.myList = myList;
}
@Override
public void run() {
for(int i = 0; i < 26; i++) {
myList.get();
}
}
}
- main 메서드를 소유한 클래스를 만들어서 스레드 2개를 실행
package com.eb1031.multithread;
public class Main {
public static void main(String[] args) {
//자원 생성
Product product = new Product();
//하나의 자원을 이용해서 2개의 스레드를 생성해서 실행
new Producer(product).start();
new Customer(product).start();
}
}
2) 실행을 하다보면 예외가 발생
- 소비자 스레드인 Customer 스레드가 리스트에 아무런 데이터가 없는데 꺼내려고 해서 발생하는 문제
- 소비자 스레드인 메서드는 데이터가 없을 때는 대기하고 생산자 스레드는 데이터를 만들어 낸 경우 데이터가 만들어졌다고 알려주어야 한다.
- 기다리기 위해서 호출하는 메서드는 wait이고 알려주는 메서드는 notify와 notifyAll이라는 메서드이다.
- 이 메서드들은 Object 클래스의 메서드이고 synchronize 메서드 안에서만 동작한다.
package com.eb1031.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) {
System.out.println(e.getLocalizedMessage());
}
//자원을 생산했다고 알려줌.
notify();
}
//소비자 메서드
public synchronized void get() {
try {
if(list.size() == 0) {
wait();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
//첫번쨰 데이터를 꺼내서 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());
}
}
}
Dead Lock
- 결코 발생할 수 없는 사건을 무한정 기다리는 것.
- synchronize 블럭이 여러개 있는 경우에 멀티스레드를 사용하면 발생.
Semaphore
- 상호배제를 하면서 동시에 수행할 수 있는 Thread의 개수를 설정할 수 있다.
- 동시에 사용할 수 있는 Thread의 개수를 설정할 수 있는 클래스.
- semaphore 클래스를 이용해서 설정
- <method>
- acquire() 리소스를 확보하는 메서드
- release() 리소스를 해제하는 메서드
1) 세마포어를 이용하지 않는 경우 - 스레드가 동시에 전부 실행
package com.eb1031.multithread;
import java.util.concurrent.Semaphore;
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("혜인"));
Thread th5 = new Thread(new SemaphoreThread("하니"));
th1.start();
th2.start();
th3.start();
th4.start();
th5.start();
}
}
package com.eb1031.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(1000);
System.out.println(message);
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
//리소스 해제
semaphore.release();
}
}
main 메서드를 소유한 클래스를 만들어서 실행 SemaphoreMain
-> 모든 스레드가 10초 동시에 메세지 출력
2) 세마포어를 이용하도록 수정.
스레드 클래스 수정.
package com.eb1031.multithread;
import java.util.concurrent.Semaphore;
public class SemaphoreMain {
public static void main(String[] args) {
Semaphore sema = new Semaphore(2);
Thread th1 = new Thread(new SemaphoreThread("민지", sema));
Thread th2 = new Thread(new SemaphoreThread("해린", sema));
Thread th3 = new Thread(new SemaphoreThread("다니엘", sema));
Thread th4 = new Thread(new SemaphoreThread("혜인", sema));
Thread th5 = new Thread(new SemaphoreThread("하니", sema));
th1.start();
th2.start();
th3.start();
th4.start();
th5.start();
}
}
정규표현식
1. 개요
- 문자열의 집합을 표현하는데 사용하는 형식 언어
- Perl에서 처음 사용
- 많은 프로그래밍 언어가 정규표현식 시능을 제공하고 있는데 언어 자체가 제공하기도 하고 별도의 라이브러리 형태로 제공되기도 합니다.
- 문자열의 패턴을 검사하기 위해서 사용합니다.
- 유효성 검사나 자연어 처리에서 많이 사용.
- java에서는 java.util.regex 패키지에 있는 Match 클래스와 Pattern 클래스 그리고 String 클래스에서 제공
- Web Programming에서는 java보다는 javascript에서 많이 사용합니다.
Network - java.net
- 다른 컴퓨터와 통신을 하기 위해서는 다른 컴퓨터의 프로그램이나 자원에 접근하기 위한 주소를 알아야 한다.
프로그램에 접근할 때는 IP와 포트 번호 또는 Domain 을 알아야 한다. - IP - 컴퓨터를 구분하기 위한 주소
- port - 프로세스를 구분하기 위한.
1) 통신절차
- 요청을 처리하는 쪽에서 포트번호를 가지고 소켓을 생성해서 시작
- 요청을 보내는 쪽에서 요청을 처리하는 쪽의 구별하기 위한 주소를 가지고 접속을 하고 요청을 전송한다.
- 요청을 처리하는 쪽에서 요청을 보내는 쪽의 데이터(parameter)를 읽어서 처리를 수행한 후 처리 결과를 요청을 보내는 쪽으로 전송한다.
* 요청을 전송하는 것을 request라고 한다. 요청을 처리하여 결과를 전송하는 것을 response라고 한다.
2) TCP 형태로 클라이언트와 서버가 대화를 주고받는 것(클라이언트가 서버에게 전송하면 서버가 다시 응답하는 것)
TCP 서버 생성
TCP Client 작성
: 자기 컴퓨터에 전송할때는 127.0.0.1 이나 localhost라고 하면 된다.
다른 컴퓨터는 IP를 물어봐야 한다.
외부의 컴퓨터에서 내 컴퓨터로 데이터가 전송이 가능하도록 할 때는 방화벽을 해제해 주어야 한다.
package com.eb1031.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~resurces 구문으로 메모리 해제를 하지 않아도 된다.
try(ServerSocket ss = new ServerSocket(8080);) {
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 msg = br.readLine();
System.out.println(msg);
//메세지를 보낼 수 있는 스트림을 생성
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//pw.print("서버가 보내는 메세지: ");
//pw.println(msg);//echo
if(msg.trim().equals("히히")) {
pw.println("하하");
}else if(msg.trim().equals("크크")){
pw.println("키키");
}else {
pw.println(msg);
}
pw.flush();
//정리
pw.close();
br.close();
socket.close();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
package com.eb1031.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("127.0.0.1"), 8080);
System.out.print("보낼 메세지: ");
String msg = sc.nextLine();
//메세지 보내기
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
pw.println(msg + "\n");
pw.flush();
//메세지 받기
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = br.readLine();
System.out.println(response);
br.close();
pw.close();
socket.close();
}
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}'국비지원 > JAVA' 카테고리의 다른 글
| [JAVA] minimini project (pc방 키오스크 프로그램 만들기 - 미완) (0) | 2022.11.07 |
|---|---|
| [JAVA] 23. Nested Class / Anonymous / Lambda / Stream API / Thread (0) | 2022.10.28 |
| [JAVA] 22. API - I/O (InputStream/OutputStream) (0) | 2022.10.27 |
| [JAVA] 21. List(stack, queue, deque) / Set / Map (0) | 2022.10.26 |
| [JAVA] 21-2. API - java.util pkg (컬렉션 프레임워크) (0) | 2022.10.25 |