단순히 jframe을 사용하여 xml,csv파일을 파싱하는 과제를 수행해보았습니다.
그 과정을 통해서 자바에 대해서 조금 더 알게 된것 같고 아직 많이 어렵다는것을 느끼게 하는 한 주 였습니다.
최근에 스프링부트와 AWS로 혼자 구현하는 웹서비스 라는 책을 구매했습니다. 시간나는 대로 틈틈히 작성하여 완수 해 볼 계획이 있습니다.
프로그램(정적)
(java) → 만들어야하는 필요한 내용이 존재하는것 → 프로그램
프로세스(Process)(동적)
개별적으로 동작하는 프로그램(Browser,Eclipse)
프로그램은 프로그램이지만 실행중인 프로그램!
쓰레드(Thread)
프로세스를 구성하는 독립적인 세부 실행 단위(Unit)
프로그램 실행을 담당하는 일꾼
멀티프로세스(Multi-Process)
여러개의 프로세스를 동시에 수행
멀티 쓰레드(Multi-Thread)
한 프로세스에서 여러개의 스레드를 동시에 수행
메인이 있는 코드는 실행이 가능하고 메인스레드가 동작해서 실행되고 (메인 스레드가 하나 → 싱글스레드 방식)
메인 이 없는 코드는 실행이 불가능하고 메인 스레드가 존재 하지 않는다
Thread는 실행 순서가 보장되지 않는다.
java에서 Thread를 만드느 방법은 2가지가 있습니다.
먼저 Runnable Interface를 Implements 하는 Class를 만듭니다.
그다음 Runnable Interface에 있는 추상 Method run()를 구현하고, 그 안에job 코드를 넣습니다.
Thead Class를 extends 하는 방법이 있습니다.
Thread Class에 있는 run method를 overriding 합니다.
run Method안에 job내용을 넣는 것은 동일 합니다.
이 경우 CoronaThread는 자체가 Thread 입니다.
run() 부모에게서 메소드안이 구현되어 있지 않은 빈껍데기 메소드이다. → 내 상황에 따라 고쳐서 사용 (overriding)
run() : Thread가 해야할 작업 내용을 가지고 있는 메소드
callback메소드 : 직접 호출되지 않고 간접적으로 호출됨 -> start가 되면 run이 동작된다.
start안에 run를 호출 하는 메소드가 포함 되어 있어서 결과적으로 run이 호출이된다 -> callback 메소드
main()이 실행되면 Main Thread가 동작합니다. 다른 Thread가 실행 되지 않으면 main Thread 하나로 프로그램이 실행됩니다.
만약, 또 다른 Thread가 실행되면 main Thread와 다른 Thread가 동시에 실행됩니다.
Main()는 start()시킨 모든 Thread 들이 종료된 후에 종료 되나요 ?
X → main()이 종료되어도 실행중인 다른 Thread가 모두 종료되어야 JVM의 실행이 종료된다.
Thread 객체를 한개만 만들고, start() method를 두번 호출하면 어떻게 될까요 ?
오류가 발생 → Thread는 내부적으로 자신의 상태를 관리하는데 , 이 상태의 오류가 발생하여 Exception을 발생시키고 그 Thread는 종료됩니다.
Thread안에 또다른 Thread를 실행 시킬 수 있나요 ?
O → 단, 공유 자원(변수,자료구조)등에 대한 각별한 주의가 필요하다.
-Thread.sleep() method는 static Method입니다.
호출 할 때 , Mili-Second (1/1000초) 단위의 시간을 파라미터로 전달하면, Thread.sleep을 호출한 Thread는 해당 시간 만큼 실행을 중지했다가 다시 실행된다 sleep(3000)는 3초 정도 정지한다.
Thread 객체는 특정 Thread 객체가 종료될 떄 까지 수행하던 job을 멈추고 특정 Thread가 종료되면 다시 job을 수행할 수 있다.
만약 A Thread 객체 a의 종료 시 까지 대기하려면 자신 Thread의 싱행 코드에 a.join 형태로 method를 호출하면된다.
Main()을 수행하는 Main Thread가 다른 Thread 종료 후 , 계속 job을 수행하는 코드
Thread 객체는 Start()가 호출되면 바로 실행되지 않습니다.
모든 Thread는 기본적으로 서로 경쟁한다.
전체 Thread는 실행 → 종료까지 다양한 상태로 존재하게되고 JVM은 이 상태를 이용해서 전체 Thread의 실행을 제어합니다.
대기 Pool에 있는 특정 Thread 객체를 방해(interrupt)하여 다시 Runnable 상태로 이동 시킬 수 있습니다.
Interrupt()를 통해 가능한데, 특정 Thread 객체의 Inuerrupt()를 호출하면 됩니다.
인터럽트를 호출이 없을경우
thread try start
Main Thread End!!
thread try end
20
인터럽트를 호출 했을경우
thread try start
thread interrupted raised
20
Main Thread End!!
public class ThreadInterruptTest {
public static void main(String[] args) {
Thread t = new Thread(20);
t.start();
t.interrupt();
try {
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main Thread End!!");
}
}
Thread.yield는 Static method 입니다.
thread의 job 수행중 지금 당장 job을 수행할 필요가 없을 경우 호출하여, Runnable 상태에 있는 다른 Thread들에게 수행을 양보한다.
단, 이 경우는 Wait상태로 가지 않고 , Runnable 상태로 이동하여 언제든 다시 경쟁을 이기고 Running 상태가 되면
Yield()호출 이후 코드를 이어서 수행합니다.
다수의 Thread가 공유 데이터에 접근해서 작업할 경우 Thread간의 간섭 현상이 발생
→ 이를 방지하기 위해서 Thread간에 동기화(Synchronization)를 시켜줌 → 일종의 LOCK
여러 Thread들이 공유하는 자원에 대한 관리는 필수이다.
어떤 Thread가 자원에 대한 중요한 Job을 실행할 떄 그 job이 완료되기 전에 다른 Thread가 접근해서 다른 job을 실행하게 되면 심각한 오류가 발생 할 수 있습니다.
Thread의 동기화를 구현하는 방법은 2가지 있습니다. ****(두가지 방법 확실히 알아보기)
공유 자원을 사용하는 중요 job을 수행하는 Method 자체를 Synchronized로 만들 수 있습니다.
이 경우 Method 수행 전체가 한개의 방해 받지 않는 단위로 만들어져서 시작 → 종료까지 다른 Thread의 자원 접근을 막을 수 있습니다. 이 때, 해당 Thread가 공유 자원에 대한 Lock을 소유했다라고 합니다. Synchronized job이 끝나면 lock을 반납합니다.
동기화 메서드는 한 순간에 하나의 쓰레드만 호출이 가능하다.
A쓰레드가 이 메소드를 호출하여 실행중인 상태에서, B쓰레드가 이 메서드를 호출하면, A가 메서드의 실행을 완료할 때까지 대기하고 있다가 완료가 되면 실행하게 된다.
public synchronized int add(int n1, int n2)
{
cnt++; //동기화가 필요한 문장
return n1+n2;
}
단점 : 실행시간이 오래 걸린다. 쓰레드의 동기화로 인해 성능이 매우 저하된다.
=> 필요한 위치에 제한적으로 사용해서 성능에 영향을 주지 않도록 구현해야 한다.
Synchronized로 선언된 메서드에는 자물쇠가 걸린다. Synchronized로 선언된 메서드를 호출하면 열쇠를 자동으로 획득되고, 메서드를 빠져나오면 열쇠는 자동으로 반납된다.
메서드의 일부(코드 블록)만 동기화가 가능하다.
public int add(int n1, int n2)
{
synchronized(this)
{
cnt++; //동기화 된 문장
}
return n1+n2;
}
동기화 메서드와 다르게 동기화 블록은 열쇠를 선택할 수 있다.
* this = key (자바의 모든 인스턴스는 하나의 열쇠를 지니고 있다.)
첫번째 필요한 열쇠는 this로 부터 얻는다. 그리고 열쇠가 더 필요한 경우에는 Object 인스턴스를 추가한다.
쓰레드의 실행순서는 소스코드가 나열된 순서와 다를 수 있다.
wait메서드는 연이은 호출이 가능하다. = 다른 쓰레드에 의해 또 다시 호출이 가능하다.
한 쓰레드가 공유 자원을 접근하는 임계영역 코드를 수행하고 있으면 다른 쓰레드는 임계 영역 코드를 수행할 수 없다.
1. Semaphore(세마포어)
1. 자원의 상태를 나타내는 카운터(0, 1)를 갖는다. - 이진 세마포어
2. initialize (세마포어 초기화), decrement (프로세스 블록), increment (블록된 프로세스를 깨움)
쓰레드가 자원을 사용하는 동안에는 세마포어 값을 변경함으로써 다른 쓰레드들을 기다리게 한다.
2. Mutex(뮤텍스)
Critical Section을 가진 쓰레드들의 실행 시간을 서로 겹치지 않게 단독으로 실행하게 하는 기술이다.
공유 자원에 대한 접근을 제한하기 위해 Locking과 Unlocking 사용.
뮤텍스 객체를 두 쓰레드가 동시에 사용 불가능하다.
Semaphore(세마포어) 와 Mutex(뮤텍스) 차이점
세마포어는 뮤텍스가 될 수 있다. 뮤텍스는 세마포어가 될 수 없다.
세마포어는 동기화 대상이 여러개 일 때 , 뮤텍스는 동기화 대상이 오직 하나일 때 사용된다.
세마포어는 소유하지 않고 있는 쓰레드가 세마포어를 해제할 수 있고, 뮤텍스는 뮤텍스를 소유하고 있는 쓰레드가 이 뮤텍스를 해제할 수 있다.
Concurrency
어떤 프로그램이나 알고리즘이 순서에 상관없이 동시에 수행되는 것이 Concurrent하다고 한다.
동시에 수행되는 Thread들이 각각 서로 다른 공유자원을 Synchronized로 확보한 후, 서로 소유한 공유자원을 추가로 요청하는 경우를 말합니다.
주로, 중첩된 Synchronized 구조에서 발생합니다. 그렇나 구조가 발생하지 않도록 Thread를 구성하고 필요한 자원은 사용후 즉시 반납 할 수 있도록 Synchronized block을 최대한 작게 구성합니다.
wait() - notify() - notifyAll()
Thread들의 공유 자원에 대한 더욱 개선된 구조를 만들 수 있습니다.
특정 자원에 대해 Lock을 반납하는 경우, Wait() method를 호출 함으로서 가능합니다.
어떤 Thread가 공유 자원에 대한 Lock을 소유하더라도, 그 시점에 바로 중요 job을 수행 할 수 없으면 Lock을 잠시 반납하면 됩니다. 단, 필요한 job을 완성하지 않았기 때문에 기다렸다가 다시 수행 합니다.
notify() ,notifyAll()을 통해서 waiting pool에서 대기중인 다른 Thread에게 이제 A자원이 다시 준비 되었음을 알릴 수 있습니다.
public class MedicineThread extends Thread{
power power;
public Thread(power power) {
this.power = power;
}
@Override
public void run() {
synchronized(power) {
if( power >= 500 ) {
try {
cell.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
if( power < 500 ) {
try {
Thread.sleep(500);
}catch(InterruptedException e) {
e.printStackTrace();
}
power = power + 100;
cell.notifyAll();
}
System.out.println("powerThread : " + power);
}
}
}
→ Lock으로 붙잡았지만 변화가 필요한 경우 wait과 notify를 이용해서 적절이 변화를 줄 수 가 있다
TCP/IP 라 함은 Internet Layer + Transprot Layer를 함께 부르는 용어이다.
TCP는 전송의 보장이 되고
UDP는 전송이 보장되지 않습니다. → Broadcating에 많이 사용된다.
IP는 4자리의 숫자로 표현된 유일한 network의 구분자 입니다. 우리가 많이 사용하는 HTTP는 TCP/IP Layer위의 Application Layer입니다. FTP등도 마찬 가지입니다.
각 Layer의 상호 통신 규약을 Protocol이라고 부릅니다.
Network는 두 컴퓨터가 연결되는 공간입니다. A , B computer가 연결되기 위해서는 Port라는 연결점이 필요하고 이 Port를 담당하는 프로그램 모듈을 Socket이라고 합니다. Port는 Http(80) FTP(21)처럼 공인된 프로토콜은 예약된 Port 번호가 있습니다.
Network 에 연결된 Computer중 무언가를 제공(service) 하면 Server라 하고, 그 Server에 접속하여, 무언가를 얻어 가면 Client라고 합니다.
Sever는 특정 ip와 특정 port를 통해서 특정 Protocol로 service하고 Client는 Server의 IP와 Port를 알고, 접속한 후 , 약속된 Protocol로 통신합니다.
대표적인 Server와 Client가 Web Server와 Browser가 있다.
IP를 기억하기 어렵기 떄문에 도메인이라는 개념을 도입해서 서비스 하고 있다.
InetAddress
import java.net.InetAddress;
import java.net.UnknownHostException;
public class NetworkInetAddressTest {
public static void main(String[] args) {
try {
InetAddress[] google = InetAddress.getAllByName("naver.com");
for (InetAddress addr : google)
System.out.println(addr.getHostAddress());
InetAddress localhost = InetAddress.getLocalHost();
System.out.println(localhost);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
→ InetAddress는 해당 도메인의 IP를 알아내는 메소드
simpleSever
package com.ssafy.network;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class NetworkSimpleServer {
public static void main(String[] args) {
int port = 5100;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("NetworkSimpleServer Started");
while (true) {
Socket socket = serverSocket.accept();
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
writer.println("Hello SSAFY!");
}
} catch (IOException e) {
System.out.println("NetworkSimpleServer exception: " + e.getMessage());
e.printStackTrace();
}
}
}
SimpleClient
package com.ssafy.network;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class NetworkSimpleClient {
public static void main(String[] args) {
String host = "localhost";
int port = 5100;
try ( Socket socket = new Socket(host, port) ) {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String message = reader.readLine();
System.out.println(message);
reader.close();
} catch ( IOException e) {
System.out.println("NetworkSimpleClient exception: " + e.getMessage());
e.printStackTrace();
}
}
}
Thread를 이용한 서버 처리
package com.ssafy.network;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class NetworkThreadServer {
public static void main(String[] args) {
int port = 5100;
//서버 소켓 생성
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("NetworkThreadServer Started");
//수락 작업 생성
while (true) {
//연결 수락
Socket socket = serverSocket.accept();
//클라이언트 접속 요청 시 객체를 생성
NetworkThread thread = new NetworkThread(socket);
thread.start();
}
} catch (IOException e) {
System.out.println("NetworkThreadServer exception: " + e.getMessage());
e.printStackTrace();
}
}
}
class NetworkThread extends Thread{
Socket client;
public NetworkThread() {}
public NetworkThread(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
OutputStream output = client.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
writer.println("Hello SSAFY!");
} catch (IOException e) {
System.out.println("NetworkSimpleServer exception: " + e.getMessage());
e.printStackTrace();
}
}
}
→try (ServerSocket serverSocket = new ServerSocket(port)) serverSocket은 autocloseble을사용하기 위해서 try안에 넣음
AWT
JDK 초기 UI API
컴포넌트가 C와 같은 Native code를 포함
SWING
스윙(Swing)은 자바에서 GUI(Graphic User Interface)를 구현하기 위해 JDK에서 기본적으로 제공하는 개발 툴킷으로 선 마이크로시스템즈의 자바 기반 클래스의 일부이다.
Event Handler
Interface 타입으로 존재 → 추상 메소드
Event Handler 이름 → ~ Listener
java.awt.event / javax.swing.event 에 속해 있다.
→ 이러한 문법을 잘 따랐을 경운 XML문서가 Well Fotmed 하다고 한다.
XML은 자유로운 tag의 생성이 가능하기 때문에, 서로 중복되는 tag가 발생할 수 있습니다.
→ 이러한 문제를 해결 하기 위해서는 XML문서는 사용 tag에 대한 prefix와 그 prefix에 대한 namespace를 사용한다.
xml 문서를 읽고 필요한 정보를 얻으려면 해당 XML에 사용된 tag를 구별하고 , 그 안에 있는 값들을 읽을 수 있어야 합니다.→ XML Parser라는 Library를 이용하여 작업한다.
javax.xml , org.w3c package를 주로 사용한다.
문서를 한번 쭉 읽으면서 tag의 발생별로 처리하는방법 (SAX)→ 다양한 탐색은 불가능
이벤트가 발생시 마다 작업
Event-Driven 방식의 Parsing
메모리에 데이터를 전부 올려 놓지 않아도 되기때문에 메모리에서 DOM에 비해 유리하다
문서를 다일고 난 후 , 문서 구조 전체를 자료구조에 저장하여 탐색하면서 처리하는 방법 (DOM)→ 복잡, 무거움
정리⇒ SAX는 빠른 반면에 한번에 처리하기 떄문에 다양한 탐색을 할 수 없습니다. DOM 방식은 다양한 탐색이 가능하지만 느리고 무겁습니다.
SAX와 DOM 둘 다 아래와 같은 Interface를 상속하고 있을 때.
public interface INewsDAO {
List<News> getNewsList(String url);
News search(String guid);
}
SAX는 Handler와 Implements의 두 구성으로 나뉘어서 구현해야한다.
SAX는 Handler와 Implements의 두 구성으로 나뉘어서 구현해야한다.
XML문서를 읽으며 해당 Tag가 들어오면 flag를 true로 바꾸어준 다음 flag가 true인 요소만 읽어서 저장하는 구조.
startElement 실행 - endElement실행 의 반복이다.
SAX Parser를 이용하기 위한 Implements
Code
public class NewsDAOSAXImpl implements INewsDAO { // 상속하는 인터페이스의 구현
List<News> news_list = new ArrayList<>();
@Override
public List<News> getNewsList(String url) {
File file = new File(url);
// SAXParserFactory의 instance를 얻어온다.
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = saxParserFactory.newSAXParser();
NewsDAOSaxHandler handler = new NewsDAOSaxHandler();
// 실제 Parsing은 Handler가 한다.
saxParser.parse(file, handler);
} catch ( ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
return news_list;
}
}
실제 사용되는 코드
DOM Parser는 SAX Parser와는 다르게 Handler가 필요 없다.
Code
public class NewsDAODOMImpl implements INewsDAO{
private List<News> news_list = new ArrayList<>();
public NewsDAODOMImpl() {}
@Override
public List<News> getNewsList(String url) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(url);
doc.getDocumentElement().normalize();
Element root = doc.getDocumentElement();
// root인 item을 얻어옴.
NodeList items = root.getElementsByTagName("item");
for( int i = 0; i < items.getLength(); i++) {
News news = new News();
Node newsNode = items.item(i);
// root의 자식노드들을 가지고와서 담음.
NodeList chNewsNodes = newsNode.getChildNodes();
// 자식노드들의 Tag확인.
for( int j = 0; j < chNewsNodes.getLength(); j++) {
Node node = chNewsNodes.item(j);
if( node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String textContent = element.getTextContent();
String nodeName = element.getNodeName();
switch ( nodeName ) {
case "title" :
news.setTitle(textContent);
break;
case "link" :
news.setLink(textContent);
break;
case "description" :
news.setDesc(textContent);
break;
case "guid" :
news.setGuid(textContent);
break;
}
}
}
news_list.add(news);
}
} catch( Exception e) {
e.printStackTrace();
}
return news_list;
}
}
실제 사용되는 부분
INewsDAO dao_dom;
dao_dom = new NewsDAODOMImpl();
News news = dao_dom.search(sc.nextLine());
List<News> list = dao_dom.getNewsList("xml링크");
→ 병합 단계 : 2개의 부분집합을 정렬하면서 하나의 집합으로 병합
→ 8개의 부분집합이 1개로 병합될 때까지 반복함
import java.lang.reflect.Array;
import java.util.Arrays;
public class Sort {
public static void mergeSort(int[] list ) {
mergeSort(list,0,list.length-1);
}
public static void mergeSort(int[] list , int start, int end) {
if(start==end) return;
//2집합으로 분할하여 각각 정렬시킴
int middle = (start + end )/2;
mergeSort(list,start,middle);
mergeSort(list,middle,end);
//정렬된 2집합을 이용하여 병함
merge(list,start,middle,end);
}
private static void merge(int[] list, int start, int middle, int end) {
int[] newArr = new int[end-start+1];
int left = start,right = middle+1;
int i = 0 ;//결과 인덱스
do {
if(list[left] < list[right]) {
newArr[i++] = list[left++];
}else {
newArr[i] = list[right++];
}
}while(left<=middle && right<=end );
//오른쪽 집합이 다 소비된경우.
while(left<=middle) {
newArr[i++] = list[left++];
}
//왼쪽집합이 다 소비되고 오른쪽 집합이 남은경우
while(right<=end) {
newArr[i++] = list[right++];
}
System.arraycopy(newArr, 0, list, start, newArr.length);
}
public static void main(String[] args) {
int[] list= {3,2,5,3,6,7};
mergeSort(list);
System.out.println(Arrays.toString(list));
}
}