Java의 라이브러리 JavaFX에 Thread를 적용해보자.
우선 Thread에 대해 아직 잘 모르다면 클릭 !
Thread 1과 Thread 2가 있다고 가정해보자.
이때 어떤 하나의 값을 Thread 1과 Thread 2에서 둘 다 다루게 된다면, 문제가 발생한다.
만약 두 Thread에서 데이터의 값을 갱신하는 코드가 있다고 가정하면,
Thread 1과 2중에서 어떤 것이 먼저 실행될지 모르기 때문에 데이터의 갱신이 의도대로 이루어지지 않는다.
이때 사용하는 것이 lock()과 unlock()이다.
이것을 사용하면 lock() -> 실행할 코드 -> unlock()이 될 동안에는 다른 Thread로 동작이 넘어가지 않는다.
그러나 lock() 이후 unlock()이 너무 오랜 시간 뒤에 실행된다면 이것 역시 문제가 될 수 있기 때문에 유의해서 사용해야 한다.
이를 실행하는 함수가 Platform.runLater()
이다.
아래의 코드를 살펴보면, 위 함수를 사용하여 btn2의 데이터 갱신을 보장한다.
public class HelloApplication extends Application{
@Override
public void start(Stage arg0) throws Exception {
//V(ertical)Box, H(orizontal) Box
VBox root = new VBox();
root.setPrefSize(400, 300); //px단위, 가로, 세로
root.setSpacing(5); //버튼과 버튼 사이의 간격
// --------------------
Button btn1 = new Button("첫번째 버튼" ); //눌렀을 때 Thread1이 만들어지도록
Button btn2 = new Button("두번째 버튼" );
//눌렀을 때 btn2의 내용이 바뀌도록
btn1.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
System.out.println(1);
btn2.setText("호랑이");
new Thread() {
public void run() {
System.out.println(2);
//btn2.setText("호랑이"); //충돌, 이를 해결하기 위해 lock()이 필요하다.
//() -> {}: 람다 함수의 기본꼴
Platform.runLater(()->{
btn2.setText("호랑이");
}); //lock()과 같은 역할, 람다 함수 사용
}
}.start();
}
});
root.getChildren().addAll(btn1,btn2);
// --------------------
//적용하기 위해서는 scene을 만들어서 등록해야 한다.
Scene scene = new Scene(root);
//scene과 arg0 연결
arg0.setScene(scene);
arg0.setTitle("Hello Jihu");
arg0.show();
}
public static void main(String[] args) {
launch();
}
}
만약 Thread 및 객체를 다른 class 전달하고 싶다면, 2가지 방법이 있다.
1) 인수로 전달하기
Thread t = new Tiger(btn2);
와 같이 인수로 btn2를 전달하고,
Tiger(Button btn){
this.btn = btn;
}
위 코드처럼 인수를 포함한 생성자를 주면 외부 class에서도 사용이 가능하다.
2) this를 통해 class 객체 전체를 넘겨버리기
Thread t = new Tiger(HelloApplication.this);
만약 전달할 변수가 너무 많다면 위 코드처럼 HelloApplication 전체를 넘겨버릴 수 있다.
Tiger(HelloApplication hello) {
this.hello = hello;
}
마찬가지로 class 객체를 포함한 생성자를 작성하면 된다.
class Tiger extends Thread {
Tiger(){}
Button btn;
HelloApplication hello;
Tiger(Button btn){
this.btn = btn;
}
Tiger(HelloApplication hello) {
this.hello = hello;
}
@Override
public void run() {
Platform.runLater(()->{
//btn.setText("호랑이"); //방법1
hello.btn2.setText("호랑이"); //방법2, this 활용, btn2를 활용하기 위해서는 btn2를 멤버 변수로 변경해 주어야 함
});
}
}
public class HelloApplication extends Application{ //여기까지 했을 떄 hello에 error가 뜨는 이유: 추상 메소드를 구현하지 않아서
Button btn1 = new Button("첫번째 버튼" ); //눌렀을 때 Thread1이 만들어지도록
Button btn2 = new Button("두번째 버튼" );
@Override
public void start(Stage arg0) throws Exception {
//V(ertical)Box, H(orizontal) Box
VBox root = new VBox();
root.setPrefSize(400, 300); //px단위, 가로, 세로
root.setSpacing(5); //버튼과 버튼 사이의 간격
// --------------------
//눌렀을 때 btn2의 내용이 바뀌도록
btn1.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
System.out.println(1);
btn2.setText("호랑이");
//방법1: 인수로 전달하기
//Thread t = new Tiger(btn2);
Thread t = new Tiger(HelloApplication.this); //자기자신, class 객체를 넘겨버림
t.start();
}
});
root.getChildren().addAll(btn1,btn2);
// --------------------
//적용하기 위해서는 scene을 만들어서 등록해야 한다.
Scene scene = new Scene(root);
//scene과 arg0 연결
arg0.setScene(scene);
arg0.setTitle("Hello Jihu");
arg0.show();
}
public static void main(String[] args) {
launch();
}
}