저번과 똑같이 Parent root = FXMLLoader.load(getClass().getResource("main.fxml"));로 변경하고


primaryStage.setTitle("Input Controls");과 같이 임의의 이름을 설정하고 난 뒤


gridpane은 행과 Pane을 행과 열로 구분하여 영역을 배분하는 판이다.


ComboBox는 아래의 공개여부 항목의 단추를 눌러 선택하는 노드이다.





이번에는 Label은 바꿀 필요가 없으므로 따로 설정은 이름말고는 없다.


그리고 좌측 하단에 보면 (X, X)식으로 행과 열의 위치가 알맞게 쓰여있음을 확인 할 수 있다.


margin 바깥 여백, padding 안 여백 두가지를 사용하면 TextField같이 우측에 조금 튀어나온 부분을 수정할 수 있다.


TextField(한 줄), fx:id : title


PasswordField, fx:id : pass


ComboBox, fx:id : comboList


DatePicker, fx:id : endDate


TextArea(여러 줄), fx:id : note


Button(저장), OnAction : save


마지막으로 Controller의 Controller class를 잊지말자


다음은 코드이다.



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

// FXML 파일(main.fxml)이 대문자가 아닌것은 class파일이 아니기때문
public class MainController implements Initializable{
	@FXML private ComboBox comboList;
	@FXML private TextField title;
	@FXML private PasswordField pass;
	@FXML private DatePicker endDate;
	@FXML private TextArea note;
	
	@Override // 거의 생성자와 비슷하다고 생각하면 된다.
	public void initialize(URL location, ResourceBundle resources) {
		System.out.println("FXML Load Complete");
		// 콤보 박스에 값 추가, 콤보박스는 처음 생성 시 따로 값이 없기 때문에
		// 파일 로드할 때 값을 추가해준다.
		ObservableList combo = FXCollections.observableArrayList("공개", "회원 공개", "비공개");
		comboList.setItems(combo);
	}
	
	public void save() { // 선택한 값과 입력한 값을 모두 출력
		System.out.println("제목 : " + title.getText());
		System.out.println("비밀번호 : " + pass.getText());
		System.out.println("공개 여부 : " + comboList.getValue()); // value는 시스템에서 임의로 부여한 값을 가져올 때 사용
		System.out.println("게시 종료 : " + endDate.getValue());
		System.out.println("내용 : " + note.getText());
	}
}
//



먼저 main의 pane 부분을


Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));


위와 같이 바꾸고 primaryStage.setTitle("buttons"); 추가해서 마음에 드는 제목을 설정한다.





위와 같이 디자인 하고 한가지 다른점은 Radio 버튼을 group으로 묶어서 다중 선택을 막는것이다.


ToggleButton : Button, Code - OnAction : toggle, Properties : group


선택 : Button, Code - OnAction : genderSel, hobbySel


남자, 여자 : RadioButton, Code - fx:id : male, female


영화, 게임, 낚시하기 : CheckBox, Code - fx:id : movie, game, fishing


성별, 취미 : Label, Code - fx:id : gender, hobby


위의 공통점을 본다면 보통 값을 읽거나 쓰기 위해선 fx:id라는 이름을 주는것과


함수에 어떠한 기능을 할 지를 설정하기 위한 노드들은 Code에 OnAction으로 설정한다는것.


마지막으로 좌측 하단의 Controller 의 Controller class 설정도 잊지말자


본인이 만든 class 파일을 설정하는 것으로 이름은 각각 다르니 알아서 찾아야함


다음은 Controller에 기능을 구현한 예제이다.


Java에 작성할 때와 같으며 사용하던 클래스 함수 등 역시 사용 가능하다.



package app;

import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;

public class MainController implements Initializable{
	@FXML private Label toggle; // @fxml 어노테이션을 기입하여 FXML의 노드임을 명시
	@FXML private Label gender;// FXML에 지정한 fx:id와 철자 하나 틀리지 않고 같이 명시를 해야함
	@FXML private Label hobby; // 대소문자도 구분
	@FXML private RadioButton male;
	@FXML private RadioButton female;
	@FXML private CheckBox movie;
	@FXML private CheckBox game;
	@FXML private CheckBox fishing;

	boolean settxt = false;
	
	@Override
	public void initialize(URL location, ResourceBundle resourceBundle) {
		System.out.println("FXML Load Complete"); // 이 문구를 꼭 입력하여 오류가 어디서 났는지 판단 기준을 잡자.
															   // FXML을 로딩하고 java를 읽는다.
	}
	
	// 라벨 아래의 버튼, 역시 OnAction에 기입한 글자와 철자하나 틀리지 않고 같이 써야함
	public void toggle() {
		String flag = toggle.getText();
		System.out.println("눌렀을 때 상태 : " + flag);
		// 누를 때 마다 ON|OFF 바꾸기
		if(settxt) {
			toggle.setText("Off");
			settxt = false;
		}else {
			toggle.setText("On");
			settxt = true;
		}
	}
	
	// 라디오 아래의 버튼
	public void genderSel() {
		if(male.isSelected()) {
			gender.setText(male.getText());
		}
		if(female.isSelected()) {
			gender.setText(female.getText());
		}
	}
	
	ArrayList list = new ArrayList();
	// 체크박스 아래의 버튼
	public void hobbySel() {
		if(movie.isSelected()) {
			list.add(movie.getText());
		}
		if(game.isSelected()) {
			list.add(game.getText());
		}
		if(fishing.isSelected()) {
			list.add(fishing.getText());
		}
		System.out.println("취미 갯수 : " + list.size() + "개");
		for(String selected : list) {
			System.out.println("선택 항목 : " + selected);
		}
		list.clear();
	}
}
//




그리고 Swing이나 awt를 사용하는게 아니기 때문에 자동완성 사용시 주의하여야 한다.


FXLM을 사용하기 때문에 꼭 JavaFx를 사용해야함




JavaFX에서 오류가 나온다면 일단 javaFX를 지정하였는지 Code에 지정한 이름들의 철자를 틀리지 않았는지부터 확인하자.










  ※ 실행 테스트를 여러번 하거나 여러개의 프로젝트를 테스트 할 때 주의점.


테스트를 하다보면 자꾸 전에 했던 프로젝트가 실행되거나 FXML의 변경사항이 저장이 안되고 실행이 될 때가 있다.


아래와 같이 해결해보자




Eclipse와 Scene Builder는 별개의 프로그램이기 때문에 수정을 했을때 서로 바로바로 적용이 되지않는다.


따로 fxml파일을 한번 열어서 저장(Ctrl + S)를 눌러주면 된다.







이클립스 좌측에서 실행하고자 하는 프로젝트를 우클릭 - Run As - Java Application을 누르면 정상적으로 실행이 된다.




이번에는 http://qdgbjsdnb.tistory.com/52 게시글을 따라서 프로젝트를 생성하자.


Java FX부터는 프로젝트 하나에 프로그램 하나라고 생각하고 프로젝트를 하나씩 생성해준다.


프로젝트가 생성됫다면 먼저 Main클래스의 BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("main.fxml")); 라인을


지우거나 주석처리 하고 Parent root = FXMLLoader.load(getClass().getResource("main.fxml")); 를 써준다.


boderpan은 사용하지 않는다. 그리고 나서 파일들중 .fxml 파일을 오른쪽 클릭하고 Scene Builder로 열어준다.





먼저 아까 코드에서 지운 BorderPane이 화면에 떠있는데 화면 왼쪽 아래에 Hierarchy에서 클릭하고 Delete를 눌러서 삭제하자.


그리고 AnchorPane을 좌측의 상단의 노드 Library에 검색하여 드래그 앤 드롭, 클릭해서 끌어다가 놔주자


※ Border Pane은 단순 배치를 하는 pane, AnchorPane은 단순 배치도 가능하고 창의 비율에 맞게 자동 조정되게끔 설정 가능


그리고 나서 등록된 AnchorPane을 클릭하면 우측에 Properties, Layout, code를 설정할 수 있는데 (항목을 클릭하면 펴지고 접혀짐)


Layout에서 Pref Width, Pref Height 항목을 300으로 설정하면 크기가 조절이 된다.


더 크게, 작게해도 상관은 없다.


다음으로는 좌측 상단의 Library 검색에 Label과 Button을 추가 할 수 있다.


Label과 Button은 우측 Layout에서 숫자를 입력할 수 있는 정사각 도형으로 바탕에서 차지하는 비율을 설정 할 수 있다.


크기를 AnchorPane처럼 설정하면 크기가 변하지 않지만


정사각 도형을 통해서 바꾼다면 창의 크기에 따라서 노드의 크기가 변한다.


전부 올렸다면 Ctrl + P 를 눌러서 확인해보자.


마지막으로 java 파일과 연결하기 위해 연결할 이름을 설정하는것으로 화면 왼쪽 하단에 Hierarchy, Controller 중 Controller을 눌러


Controller class에 단추에서 원하는 [패키지명].[Controller클래스] 골라서 선택한다.


그리고 나서 꼭 저장 (Ctrl + S)을 해야한다. 저장을 안해서 오류가 자주 발생한다.


이렇게 디자인과 설정은 끝난다.


처음이라서 장황하게 설명했지만 다음부터는 결과만 봐도 바로바로 만들 수 있을것이다.


다음으로는 우측 리스트의 Code 부분의 설정이다.




Label을 누르고 code 부분에서 fx:id에 msgLabel을 입력한다.


Code 부분에 입력하는것은 java 파일로 기능이나 변수 선언시에 노드들을 구분하기 위해서 설정하는 것으로 철자하나 틀리면 인식을 


못하니 오타에 특히 주의를 두어야 한다.


다음은 버튼에 기능을 부여하기 위한 작업이다.





On Action에 이름을 부여하고 저장을 하면 된다.


정리하면 fx:id에 부여한 이름으로 변수 선언하고 On XXXXX에 부여한 이름으로 메소드를 선언한다.



package app;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class MainController implements Initializable{ // 시작하는 인터페이스
	int i = 0;
	
	// fx:id와 변수명이 일치해야 알아먹기 때문에 주의, 어노테이션 @FXML을 써주어 FXML 노드임을 명시
	@FXML
	private Label msgLabel;
	
	
	// FXML 로드 완료시 제일 먼저 실행되는 메소드 (생성자 느낌)
	@Override
	public void initialize(URL location, ResourceBundle resources) { // implement의 생성자 오버라이딩
		System.out.println("FXML Load Complete"); // java 코드까지 무사히 로드 되었는지 확인
	}
	
	// OnAction과 연결되는 메소드, OnAction Id와 맞춰주자
	public void printMsg() {
		i++;
		msgLabel.setText("버튼 클릭!! " + i + "번");
		System.out.println("버튼 클릭!..!");
	}
	
}



Java Fx 작성시 UI가 핵심인데 UI 정보를 손으로 직접 입력하고 생각 하는것은 정말 어려운 일이다.


그러기 위해서 Scene Builder를 사용하여 디자인 하는것인데


그 전에 UI가 어떠한 느낌으로 작성되는지 알아보기 위해 한번 사용하지 않고 작성해보자


먼저 프로젝트를 만들 때 project name을 설정하고 바로 finish를 눌러서 생성하자


http://qdgbjsdnb.tistory.com/52 게시글의 3번째까지만 진행하면 된다.


프로젝트가 생성이 되면 src폴더가 생성이 되는데 그 안의 패키지와 java파일 css파일 중에서 java 파일만 사용한다.


생성하면 Main에 기본 틀이 만들어져 있는데 아래 예제의 주석문을 통해 설명한다.



package application;
	
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;


public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		// 컨트롤 -> 레이아웃 -> 씬으로 촬영 -> 특수효과 -> 제목 -> 상영
		// 자신을 -> 셀카구도 ->     촬영      -> 포토샵    -> 제목 -> SNS 게시
		try {
			// 먼저 바닥이 될 판을 선언
			// 빈 판자 레이아웃
			BorderPane root = new BorderPane();
			
			// 촬영(구도, 크기), 작업 할 공간을 판에 넣고 크기 설정
			Scene scene = new Scene(root,400,400);
			
			// 특수효과 또는 포토샵, css파일은 그래픽 작업이나 특수 기능을 넣는데 따로 다루지 않을 계획
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			
			// 제목 + 업로드(홍보), stage의 제목을 설정하고 scene을 올림.
			primaryStage.setTitle("Hello JavaFX");
			primaryStage.setScene(scene);
			
			// 상영하기, scene이 시작된다.
			primaryStage.show();
			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}


위의 코드를 실행시키면 java 프로그램이 하나 실행되고 그 제목은 Hello JavaFX일 것이다.


java fx의 기본 틀은 이러한 느낌으로 구성이 된다.


다음으로는 버튼을 하나 만들어보자



package application;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

	@Override
	public void start(Stage primaryStage) {
		// 자신을 - 구도 및 촬영 - 뽀샵 - 게시
		// 1. 컨트롤, 버튼을 생성하여
		Button btn = new Button(); // 버튼에 띄울 메세지는 클릭이며
		btn.setText("클릭");

		// 버튼에 이벤트 추가, 버튼을 눌렀을때 작동하는 함수는 이러하다.
		btn.setOnAction(new EventHandler<ActionEvent>() {
			int i = 0;
			@Override
			public void handle(ActionEvent event) {
				System.out.println("Hello javaFX");
				i++;
				btn.setText(i + "번");
			}
		});
		
		// 2. 레이아웃 + 컨트롤
		StackPane root = new StackPane();
		root.getChildren().add(btn); // 버튼을 판에 올려준다.
		
		// 3. 씬
		Scene sc = new Scene(root, 300, 200);
		
		// 4. 특수효과(생략)
		// 5. 제목(stage)
		primaryStage.setTitle("버튼 테스트");
		
		// 6. 상영(stage)
		primaryStage.setScene(sc);
		primaryStage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
}


다음으로는 라벨과 텍스트필드 등을 이용하여 로그인 화면을 구현해보자



package application;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;


public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		// Control
		// 제목
		Text txt = new Text("로그인");
		txt.setFont(Font.font(20));
		
		// 라벨과 텍스트 입력 필드
		Label userId = new Label("ID : ");
		TextField inputId = new TextField();
		// 라벨과 비밀번호 입력 필드
		Label userPw = new Label("PW : ");
		PasswordField inputPw = new PasswordField();
		
		// 버튼 추가
		Button btn = new Button("log in");
		HBox hbBtn = new HBox(10);
		hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
		hbBtn.getChildren().add(btn);
		// 경고 메세지가 나올 위치
		Text actionTarget = new Text();
		// 버튼에 이벤트 추가
		btn.setOnAction(new EventHandler<ActionEvent>() {
			
			@Override
			public void handle(ActionEvent arg0) {
				actionTarget.setFill(Color.RED);
				actionTarget.setText("회원 가입 해 주세요.");
			}
		});
		
		// 2. layout
		GridPane grid = new GridPane();
		grid.setAlignment(Pos.CENTER);
		grid.setHgap(10);
		grid.setVgap(10);
		grid.setPadding(new Insets(25,25,25,25));
		
		
		grid.add(txt,  0, 0, 2, 1); // cols, rows, colspan, rowspan
		grid.add(userId, 0, 1);
		grid.add(inputId,  1, 1);
		grid.add(userPw,  0, 2);
		grid.add(inputPw,  1, 2);
		
		// 6. 버튼을 레이아웃에 추가
		grid.add(hbBtn, 1, 3);
		grid.add(actionTarget, 1, 4);
		
		// 3. scene
		Scene scene = new Scene(grid, 300, 275);
		
		primaryStage.setTitle("로그인 화면");
		primaryStage.setScene(scene);
		primaryStage.show();
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}


위와 같이 정말 간단한 ui이지만 써야할게 정말 많다.


일반적인 프로그램에 있는 ui를 생각해보자.


ui만 만드는데 꼬박 3일은 걸릴것만 같다.


그래서 이제부터 사용하게될 Scene Builder를 배워보자





1.JavaFX 프로젝트 생성


먼저 사진을 보자



자바 프로젝트와 똑같이 Project를 눌러 리스트를 불러오자




JavaFX Project를 누르고 Next>를 누르자.




프로젝트 명을 입력하고 Next>




이 화면에선 아무것도 건드리지 말고 Next>





Application type : Desktop / Mobile 지금은 Pc환경으로 만들것이므로 Desktop 설정


Package Name : Java 프로젝트에서 만들던 Package와 동일한 개념


Language : NONE, FXML 등이 있는데 우리가 사용할 언어는 FXML


Root-Type : UI에 사용할 Pane을 말함


FileName : Scene Builder , UI 파일의 이름


Controller Name : UI의 노드(버튼, 라벨, 라디오 버튼 등 인자)들의 기능을 구현 할 자바파일


마음에 드는대로 설정을 하고 Finish를 눌러 마무리 하면 된다.




2.Scene Builder 실행


위에서 설정한 FileName 파일을 src에서 찾아 오른쪽 클릭을 하고




Open with SceneBuilder을 누르면 실행이 된다.





1.Scene Builder 설치 및 설정


UI를 직접 손으로 코딩하는일은 매우 번거롭고 위치 지정하기도 어렵다.


그러므로 UI를 간편하게 만들고 위치도 잡아주는 툴을 이용하여 만들것인데 SceneBuilder를 이용하여 만들것이다.


다음 사이트로 접속하면 쉽게 다운 받을 수 있는데


주의할점은 Java 8버전으로 받는것이다. 자신이 설치한 JDK 버젼에 맞추면 된다.


http://gluonhq.com/products/scene-builder/






사이트로 들어가면 위 화면이 나오는데 마우스 휠을 이용하여 아래로 내리면


Java 8버전 전용으로 나온것을 'Download'하면 된다.


설치에는 따로 어려운 부분이 없고 다음 다음만 누르면 되는데


그 중 경로 설정이 나올 경우에는 꼭 기억해두자.


default 경로는 C:\Program Files\Scene Builder이다.




2.Eclipse에 Scene Builder 적용


이클립스를 먼저 실행시키고 상단 메뉴바를 보자




상단 메뉴바의 Window - Preferences 설정을 누르고





JavaFX의 SceneBuilder executable이 비어있을 것이다.


Browse...를 눌러서 아까 Scene Builder를 설치할 때 설정한 경로를 찾아




SceneBuilder.exe를 설정해주고 Apply 버튼을 누르고 재시작 하면 적용이 끝난다.


0.Workspace 재설정


Java FX는 새로운 파트이고 여태껏 java 공부를 하느라 가득찬 workspace를 새로 만들면 프로젝트 성격별로 구분하기도 좋다.


workspace 재설정에는 두 가지 방법이 있다.




-  0-1 -


Eclipse 처음 시작시에 'Use this as the default and do not ask again'을 체크하지 않은 사용자의 경우는





경로만 새로 입력하고 Launch를 누르면 새로운 workspace가 만들어진다.




-  0-2 -


'Use this as the default and do not ask again'를 체크하였거나 이클립스 사용 도중에 workspace를 바꾸고 싶을 때




상단 메뉴바 - File - Switch Workspace


목록을 보면 위에는 최근에 사용했던 workspace가 나오고 Other라는 목록이 또 있다.


전자는 예전에 사용한 workspace를 바로 여는것이고 후자는 0-1의 사진에서 나오는 처음 시작 했을때의 박스가 나오고 새로운 경로를 입력하면 된다.





workspace를 새로 만든다면 설정도 새로 해줘야 한다.


workspace마다 설정이 별개로 등록되기 때문에 각각 설정을 따로 해주어야 한다.


설정의 경우 한번 해 놓으면 다시 안보기 때문에 기억이 안 날수도 있다.


특히 인코딩 설정(UTF-8)은 필수이므로 꼭 다시 해놓아야 한다.


http://qdgbjsdnb.tistory.com/5?category=714965


예전에 올렸었던 게시글을 참조하자.




1.JavaFX 다운로드


workspace를 새로 만들고 인코딩 설정을 끝냈다면 다음은 UI 작업을 위한 JavaFx 다운로드를 해야한다.






Eclipse 상단 메뉴바의 Help - Eclipse Marketplace... 를 클릭하면 아래 사진의 박스가 나오는데








Search 탭의 Find에 fx라고 검색하면


목록에 e(fx)clipse 3.0.0 (버젼은 바뀔수도 있다.)이 검색되는데 Install 버튼을 눌러 설치하면 된다.



Thread Pool은 다수의 스레드 자원 할당을 좀 더 효율적으로 하는 작업이다.


Thread pool을 생성 할 때 스레드 수를 지정하고


Runnable 작업이나 Callable 작업을 받고 실행된다.


단순하게 thread.start를 한다면 os의 작업 스케줄링으로 작업이 진행되는데


pool은 작업 할 Thread 수를 미리 설정해서 스케줄링이 진행된다.


하지만 Thread 수를 잘 못 예측하여 높게 잡는다면 오히려 메모리 공간이 낭비된다.




Runnable 작업은 Return값이 없고


Callable 작업은 Return값이 있는게 특징이다.


생성된 작업은 submit(), execute()로 실행되고


execute는 반환값이 없고 예외가 발생하면 스레드가 제거되는 반면


submit은 Future타입을 반환하고 예외상황에도 스레드를 재사용한다.




Thread Pool은 main Thread가 종료되도 남아있기 때문에 상태를 확인하고 따로 종료를 해주어야 한다.


shutdown()은 현재 작업을 마무리 후 종료시키고 shutdownNow()는 interrupt를 일으켜 강제 종료시킨다.



package chap10.exam13.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {

	public static void main(String[] args) {
		// ThreadPool 생성 방법
		// 1개 이상의 스레드가 추가 되었을 때 60초 이상 놀고 있으면 제거
		// 작은 캐쉬 메모리 방에서 오래 붙들고 있으면 낭비가 심하기 때문에 제거
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 실행환경, 현재 이용할 수 있는 스레드, 프로세서 불러오는 방법
		// 현재 CPU에서 코어 갯수를 추출하는 방법.
		int n = Runtime.getRuntime().availableProcessors(); 
		System.out.println("가용 할 수 있는 스레드 갯수 : " + n);
		// 사용할 스레드, 프로세서 갯수를 지정하는법
		// 기본적으로 n개의 스레드를 유지
		pool = Executors.newFixedThreadPool(n);
	}

}







package chap10.exam14.exec;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecuteMain {

	public static void main(String[] args) throws InterruptedException {
		// Runnable로 작업 요청
		// execute() : 용서 X, 반환 X // 혼자 실행
		// submit() : 용서 O, 반환 O // 전송에는 답변이 있어야 함
		
		// 스레드 순서가 중요
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {
				System.out.println("Runnable 처리");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X
		
		
		// 4. 스레드 풀 종료(close)
		// 처리중인 작업이 마무리 되면 닫는다.
		pool.shutdown();
		// 처리중인 작업에 관계없이 강제로 닫는다.
//		pool.shutdownNow();
		// shutdown()이 타임아웃안에 실행 되는지 반환
		// 제한 시간안에 작업 완료시 true를 반환
		// 만약 제한시간을 지날경우 강제로 닫음
//		boolean t = pool.awaitTermination(10L, TimeUnit.SECONDS);
		
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Submit {

	public static void main(String[] args) throws Exception {
		// 1. 스레드 풀 생성
		ExecutorService pool = Executors.newCachedThreadPool();
		
		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("Callable 처리");
				return "작업완료";
			}
		};
		// 3. 실행
		Future<String> submit = pool.submit(callable);
		System.out.println(submit.get());
		
		// 4. 스레드 풀 닫기
		pool.shutdown();
		
	}

}


package chap10.exam14.exec;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestPool {

	public static void main(String[] args) throws Exception {
		// Thread Pool 10번 출력하기

		// Runnable 사용, 순차실행
		// 1. 스레드 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 할 일 지정
		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		// 3. 실행
		pool.execute(runnable); // 반환 값 X

		// 4. 스레드 풀 종료(close)
		pool.shutdown();





		// Callable 사용, 자기 마음대로 실행
		// 1. 스레드 풀 생성
		ExecutorService pool2 = Executors.newCachedThreadPool();

		// 2. 할 일 만들기
		Callable<String> callable = new Callable<String>() {

			@Override
			public String call() throws Exception {

				System.out.println("Thread Pool1");

				return "작업완료";
			}
		};

		// 3. 실행
		Future<String> submit = null;
		for(int i =0; i < 10; i++) {
			System.out.print(i + " : ");
			submit = pool2.submit(callable);
		}
		System.out.println(submit.get());

		// 4. 스레드 풀 닫기
		pool2.shutdown();

	}

}



Thread Pool은 runnable이나 callable에 관계없이 작업이 완료되면 Future객체를 반환한다.


그 Future 객체를 가져오는 get 메소드는 join과 같은 역할을 수행하고


이러한 작업을 Pool Blocking이라고 한다.



package chap10.exam15.block;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class NoResultMain {

	public static void main(String[] args) throws Exception {

		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);

		Runnable runnable = new Runnable() {

			@Override
			public void run() {
				for(int i = 0; i < 10; i++) {
					System.out.println("Thread Pool " + (i + 1) + "번");
				}
				System.out.println("----------------------------");
			}
		};
		
		Future future = pool.submit(runnable); // 반환할게 따로 없으므로 제너릭 지워줌
		// 반환값이 없는데 get() 사용 이유
		future.get(); // get() == join() / 이 아래 내용이 반환 후 실행
		System.out.println("작업 완료");
		pool.shutdown();
	}

}


package chap10.exam15.block;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ResultMain {

	public static void main(String[] args) throws Exception {
		// 1. 풀 생성
		int n = Runtime.getRuntime().availableProcessors();
		ExecutorService pool = Executors.newFixedThreadPool(n);
		// 2. 해야 할 일
		Callable<Integer> task = new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 1; i <= 100; i++) {
					sum += i;
				}
				return sum;
			}
		};
		// 3. 실행
		Future<Integer> future = pool.submit(task);
		int result = future.get();	
		System.out.println("합 : " + result);
		// 4. 풀 닫기
		pool.shutdown();
	}

}





1.데몬 스레드, Demon Thread


Main이 종료 될 때 직접 작성한 스레드들은 따로 끝내지 않으면 계속 돌아가게 되는데


Thread가 많아 질 수록 리소스를 많이 가져가고


꺼지지 않은 Thread가 만약 공유하는 변수값을 수정한다면 프로그램 자체가 꼬이게 된다.


그래서 스레드 start 전에 setDaemon(true) 설정을 하면


스레드를 실행한 메인 스레드가 종료된다면 강제로 같이 종료가 된다.


다음 예를 보자


package chap10.exam12.demon;

public class DemonThread extends Thread {

	@Override
	public void run() {		
		while(true) {
			System.out.println("Demon Thread Running...");
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}
<


package chap10.exam12.demon;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		Thread demon = new DemonThread();
		demon.setDaemon(true);//work -> demon
		demon.start();//데몬 스레드 시작
		Thread.sleep(3000);//3초후
		System.out.println("메인 스레드 종료");
	}

}


1.State


State는 Thread의 작업을 감시하는 역할을 한다.


스레드의 상태가 어떤지 지속적으로 알려주는 기능이다.


활용도는 낮은편..



package chap10.exam06.state;

public class WorkThread extends Thread {

	@Override
	public void run() {

		for(long i=0; i<1000;i++) {
			System.out.println(i);
		}

		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		for(long i=0; i<1000;i++) {
			System.out.println(i);
		}

	}
	
}
package chap10.exam06.state;

public class Main {
	
	private static Thread.State state;

	public static void main(String[] args) throws InterruptedException {
		/*워크 스레드 : 생성 -> 실행 -> 일시정지 -> 실행 -> 종료*/
		/*메인스레드 : 워크스레드를 감시*/
		WorkThread work = new WorkThread();

		//메인 스레드는 지속적으로 워크 스레드를 감시
		while(true) {
			state = work.getState();
			System.out.println("THREAD STATE : "+state);
			//객체 생성시 워크 스레드 실행
			if(state == Thread.State.NEW) {
				work.start();
			}			
			/*Runnable 이 순식간에 진행 되므로 잘 잡히지 않는다.
			Sleep 을 제거 하면 볼수는 있다.
			*/			
			Thread.sleep(500); //0.5초 휴식
			
			if(state == Thread.State.TERMINATED) {
				break;
			}
			
		}

	}

}





2.Control(sleep, yield, join, wait, notify, stop, interrupt)


2-1 sleep, yield


sleep은 Thread를 대기 시키는 메소드로 안에 인자를 받는데 mil second로 1000값을 주면 1초동안 Thread를 대기 시킨다.


yield는 작업을 셀프 인터럽트 시키는 메소드로 작업을 다음 순서 스레드에게 양보한다고 이해하면 된다.



package chap10.exam07.yield;

public class ThreadA extends Thread {
	
	boolean stop = false;
	boolean yield = false;

	@Override
	public void run() {
		
		while(!stop) {
			System.out.println("Thread A 동작");
			if(yield) {
				System.out.println("Thread B 에게 양보");
				Thread.yield();
			}			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Thread A 중지");

	}
	
	

}
package chap10.exam07.yield;

public class ThreadB extends Thread {
	
	boolean stop = false;
	boolean yield = false;

	@Override
	public void run() {
		
		while(!stop) {
			System.out.println("Thread B 동작");
			if(yield) {
				System.out.println("Thread A 에게 양보");
				Thread.yield();
			}			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Thread B 중지");

	}

}
package chap10.exam07.yield;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		/*
			yield 는 자신이 쉬겠다는 의미가 아님
			다른 스레드에게 할 수 있는 기회를 양보 하는 것
			실행 순서 정도에 변화를 줄 수 있다.
		*/

		//A,B 스레드 생성 후 실행
		ThreadA thA = new ThreadA();
		ThreadB thB = new ThreadB();
		thA.start();
		thB.start();		
		
		//A 에게 양보 하라고 함(B 에게 우선권 양보)
		thA.yield = true;
		Thread.sleep(1000);
		//B 에게 양보 하라고 함(A 에게 우선권 양보)
		thB.yield = true;
		Thread.sleep(1000);		
		
		//A,B 정지
		thA.stop = true;
		thB.stop = true;	
		
	}

}



2-2. join


스레드가 병렬로 처리 될 경우 작업 자체가 분리되는 부분이 있는데 join을 사용하면 작업이 공통 처리된다.


다음 예제는 메인 스레드와 oper 스레드가 join을 사용했을 때와


주석처리하여 사용하지 않았을 때의 결과를 비교하는 예제이다.


한번 직접 oper.join(); 을 주석처리하여 테스트 해보자.



package chap10.exam08.join;

public class OperThread extends Thread{

	private int sum = 0;
	
	@Override
	public void run() {		
		for(int i=1; i<=100; i++) {
			sum += i;
		}
	}

	public int getSum() {
		return sum;
	}

}
package chap10.exam08.join;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		//1~100 까지의 합
		OperThread oper = new OperThread();
		oper.start();//oper : 실제 계산 수행
		
		oper.join();//oper 와 같이 가라, 이 줄을 주석처리 하여 테스트
		//oper 의 수행 내용이 끝나고 나면 아래가 실행 된다.
		//main : 계산 내용을 출력
		System.out.println("1~100 의 합 : "+oper.getSum());
	}

}




2-3. wait, notify


wait은 스레드를 대기상태로 만들고


notify는 스레드 중 하나를 실행대기 상태로 만들고


notifyall은 스레드 전체를 실행대기 상태로 만든다.


대기상태는 작업을 일시정지 하는것이고 실행대기는 작업 대기열에 넣어 연산을 준비하는 것이다.



package chap10.exam10.test;

public class CommonData {
	
	private String data = null;
	//wait(), notify() 등을 호출하기 위해서는 synchronized 필요
	public synchronized void getData() {
		try {			
			if(data != null) {//1. 데이터가 있으면 가져온다.
				System.out.println("가져온 데이터 : "+this.data);
				this.data = null;//가져왔으면 비워주고
			}			
			notify();	//2. 깨우고
			wait();		//3. 난 쉰다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}	
	}

	public synchronized void setData(String data) {		
		try {
			//1. 데이터가 없으면 만든다.
			if(this.data == null) {
				System.out.println("넣을 데이터 : "+data);
				this.data = data;
			}			
			notify();	//2. 넣고 나면 깨우고
			wait();		//3. 난 쉰다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}	

}
package chap10.exam10.test;

public class Consumer extends Thread{
	
	private CommonData data;

	public Consumer(CommonData data) {
		this.data = data;
	}

	@Override
	public void run() {
		for(int i=1; i<=5;i++) {
			data.getData();
		}
	}	

}
package chap10.exam10.test;

public class Producer extends Thread{
	
	private CommonData data;

	public Producer(CommonData data) {
		this.data = data;
	}

	@Override
	public void run() {
		for(int i=1; i<=5;i++) {
			data.setData("DATA_"+i);
		}
	}	

}
package chap10.exam10.test;

public class Main {

	public static void main(String[] args) throws InterruptedException {
		
		CommonData data = new CommonData();
		
		Producer pro = new Producer(data);
		Consumer con = new Consumer(data);
		pro.start();
		Thread.sleep(10);
		con.start();

	}

}






2-4. stop, interrupt


stop과 interrupt는 Thread 작성 시 run에서 작업중인 내용일 종료시켜버린다.


Thread는 보통 while(true)를 통해서 무한루프를 돌려 감시체계를 만든다던가 특정 작업을 받기위해 대기하고 있다던가


그러한 경우로 자주 쓰이기 때문에 강제 종료해주는 기능이 필요할 때가 있다.


이때 stop() 을 사용 할 수 있으나 현재는 사용중지를 권고 하고 있다.



package chap10.exam11.stop;

public class Inter extends Thread {

	@Override
	public void run() {
		/*
		try {
			while(true) {
				System.out.println("Inter 실행 중");
				Thread.sleep(1);
			}
		} catch (InterruptedException e) {
			System.out.println("자원 정리");
			System.out.println("종료");
		}
		*/
		while(true) {
			System.out.println("Inter 실행 중");
			if(Thread.interrupted()) {
				break;
			}
		}
		System.out.println("자원 정리");
		System.out.println("종료");
		
	}
	
	

}
package chap10.exam11.stop;

public class StopFlag extends Thread {	
	
	private boolean stop = false;
	
	public void setStop(boolean stop) {
		this.stop = stop;
	}
	
	@Override
	public void run() {		
		while(!stop) {
			System.out.println("StopFlag 실행중");
		}
		System.out.println("자원 정리");
		System.out.println("종료");
	}
}
package chap10.exam11.stop;

public class MainThread {

	public static void main(String[] args) throws InterruptedException {
		/*Stop Flag 이용*/
		StopFlag flag = new StopFlag();
		flag.start();
		Thread.sleep(1000);
		flag.setStop(true);
		
		/*강제 인터럽트 이용*/
		Inter inter = new Inter();
		inter.start();
		Thread.sleep(1000);
		inter.interrupt();//강제로 인터럽트 예외를 발생
			

	}

}


'개념 및 코딩 > 08.Thread 스레드' 카테고리의 다른 글

[JAVA]08-07.Thread Pool, Pool Block  (0) 2018.09.06
[JAVA]08-05.Demon Thread  (0) 2018.09.06
[JAVA]08-03.Priority, Synchronized  (0) 2018.09.06
[JAVA]08-02.Runnable, Thread  (0) 2018.09.04
[JAVA]08-01.Thread  (0) 2018.09.04

+ Recent posts