05.19 스레드,네트워크
동기화 메소드 및 동기화 블록 – synchronized
단 하나의 스레드만 실행할 수 있는 메소드 또는 블록
다른 스레드는 메소드나 블록이 실행이 끝날 때까지 대기해야
package t04_sync;
public class ThreadSyncEX {
public static void main(String[] args) {
Runnable work = new WithDrawThread();
Thread thread1 = new Thread(work);
Thread thread2 = new Thread(work);
thread1.setName("스레드 1");
thread2.setName("스레드 2");
thread1.start();
thread2.start();
}
}
class Account{
// 현재 등록된 금액
private int balance = 10000;
public int getBalance() {
return this.balance;
}
// 출금
//synchronized
// public synchronized boolean withdraw(int money) {
public synchronized boolean withdraw(int money) {
// 출금 가능한 금액
if(balance >= money) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance -= money;
return true;
}
// 출금 할 수 없는 금액
return false;
}
}
// 출금 작업 스레드 기능 구현 객체
class WithDrawThread implements Runnable{
Account account = new Account();
@Override
public void run() {
while(account.getBalance() > 0) {
// 1000 ~ 5000 : 출금 금액 랜덤 생성
int money = (int)(Math.random() * 5 + 1 ) * 1000;
// Account 클래스를 직접적으로 수정하지 못하는 경우
boolean isDenied = false;
// 임계영역 지정
synchronized (account) { //lock
isDenied = account.withdraw(money);
}
if(!isDenied) {
System.out.println("출금 금액 부족 거부");
}else {
System.out.printf(
"%s 출금 : %d원 남은 금액 : %d원 %n",
Thread.currentThread().getName(),
money,
account.getBalance()
);
}
}
}
}
스레드간 협업 – wait(), notify(), notifyAll()
동기화 메소드 또는 블록에서만 호출 가능한 Object의 메소드
두 개의 스레드가 교대로 번갈아 가며 실행해야 할 경우 주로 사용
package t05_control_method.notify_wait;
public class DataBox {
private String data;
// notify, wait 은 임계영역 안에서만 사용 가능함.
synchronized String getData() {
if(this.data == null ) {
try {
wait();
} catch (InterruptedException e) {}
}
String value = this.data;
this.data = null;
System.out.println("읽은 데이터 : " + value);
notify();
return value;
}
synchronized void setData(String data) {
if(this.data != null) {
try {
wait();
} catch (InterruptedException e) {}
}
this.data = data;
System.out.println("생성한 데이터 : " + data);
notify();
}
}
package t06_stop_thread;
// 스레드 안전하게 종료시키는 방법 1 - flag이용
class PrintThread extends Thread{
private boolean isRun = true;
public void setIsRun(boolean isRun) {
this.isRun = isRun;
}
@Override
public void run() {
while(isRun) {
System.out.println("실행중....");
}
System.out.println("자원정리");
System.out.println("실행종료");
}
}
public class StopFlagExample {
public static void main(String[] args) {
PrintThread t = new PrintThread();
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
// 플래그를 이용하면 run메소드 안전하게 실행하고 정지
t.setIsRun(false);
}
}
package t06_stop_thread;
// 스레드 안전하게 종료시키는 방법 2
class InterruptThread extends Thread{
public void run() {
// 외부에서 이 스레드를 중단시키라고 interrupt 호출하면 true로 변경됨.
boolean isInterrupted = Thread.interrupted();
System.out.println(isInterrupted);
while(true) {
System.out.println("실행 중 -1");
isInterrupted = Thread.interrupted();
if(isInterrupted) {
break;
}
}
System.out.println("자원해제");
System.out.println("실행종료");
}
}
public class InterruptedExample {
public static void main(String[] args) {
InterruptThread it = new InterruptThread();
it.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
it.interrupt();
}
}
package n1_socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientExample {
public static void main(String[] args) {
//소켓 생성 시 ip주소, port번호 입력 & server에 연결됨
try {
System.out.println("[ server에 연결 요청]");
// socket : 서버에 대한 정보 전달 받음
Socket socket = new Socket("10.100.205.177",5001);
System.out.println("[server 연결 성공]");
InputStream is = socket.getInputStream();
byte[] bytes = null;
String message = null;
bytes = new byte[100];
int readByteCount = is.read(bytes); // stream에서 값이 전달될때까지 blocking
message = new String(bytes,0,readByteCount,"UTF-8");
System.out.println("[데이터 받기 성공]" + message);
OutputStream os = socket.getOutputStream();
message = "2분남았다";
bytes = message.getBytes("UTF-8");
os.write(bytes);
os.flush();
System.out.println("[데이터 발신 완료]");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package n1_socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerExample {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(5001);
while(true) {
System.out.println("Client 연결 대기중");
//client에 연결요청 보낸 client 정보가 들어감.
Socket client = server.accept(); // client의 연결요청이 올때까지 blocking.
System.out.println("client 연결 수락");
//getRemoteSocketAddress: client 정보를 추상클래스로 반환해줌
InetSocketAddress isa
= (InetSocketAddress)client.getRemoteSocketAddress();
//getHostName() : client 이름 정보
System.out.println(" - "+isa.getHostName());
byte[] bytes = null;
String message = null;
OutputStream os = client.getOutputStream();
message = "또 올거냐??";
bytes = message.getBytes("UTF-8");
os.write(bytes);
os.flush();
System.out.println("client에 데이터 발송");
InputStream is = client.getInputStream();
bytes = new byte[100];
int readCount = is.read(bytes);
message = new String(bytes,0,readCount,"UTF-8");
System.out.println("client에서 전달 받은 데이터 : "+ message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}