-
쓰레드(Thread)Java 2023. 2. 19. 13:35
학습 목표
프로세스의 용어에 대한 이해
thread 의 개념
thread를 만드는 방법 학습
thread를 사용하는 방법 학습
thread의 수행 순서는 개발자가 완전히 제어할 수 없는 부분 이다.thread 란?
하나의 프로세스 안에 실제 작업을 수행하는 단위이다.
하나의 프로세스는 하나 이상의 thread를 가지게 되고, 실제 작업을 수행하는 단위는 thread이다.
process
프로그램이 실행되면 OS로 부터 메모리를 할당 받아서 프로세스 상태가 된다.
프로세서 안에는 하나의 작업에 단위인 여러 Thread를 가질 수 있다.
multi threading
여러 thread가 수행되는 프로그래밍, 마치 여러 작업이 동시에 실행되는 효과를 얻을 수 있다.
(하나의 작업만 하는데 너무 빨리 작업되서 여러 작업이 동시에 하는것처럼 보인다)
thread는 각각에 자신만에 작업 공간을 가진다. (context 영역)
두개의 쓰레드가 같은 객체에 접근 할 경우, 동시에 접근 함으로써 오류가 발생
(동시에 하면 더하는 작업자랑 빼는 작업자를 만들었는데 일이 겹쳐서 기본값에 더하고 빼고 된다.)
그러면 어떻게 해줘야 하나?
현재 이 메서드에 속해있는 객체에 lock을 걸어주면 된다.
각 thread 사이에 공유하는 자원이 있을 수 있다(코딩에 따라서)
여러 thread가 자원을 공유하는 작업이 수행되는 경우 서로 자원을 차지 하려는
race condition이 발생할 수 있다 (--> 의도하지 않은 결과를 생성할 수 있다)
이렇게 여러 thread가 공유하는 자원중 경쟁이 발생하는 부분을 critical section 이라고 한다.
critical section 을 해결하려 다루기 위해서 동기화 처리를 하는 것이 바람직하다. (일종에 순차적 수행을 맡긴다.)
단일 쓰레드 실행
package ch01; public class ThreadText1 { // 메인 쓰레드 public static void main(String[] args) { // 작업에 단위 쓰레드 동작 for (int i = 0; i < 30; i++) { // System.out.println("i : " + i + "\t"); System.out.print("-"); try { // 너무 빠르니까 0.5초동안씩 잠들어라고 쓰레드 한테 명령 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } // end of main } // end of class쓰레드 만들어보기 (생성)
package ch01; //쓰레드를 상속한 클래스이다. 즉 Worke는 Thread 이기도 하다. - 다형성 class Woker extends Thread { private String name; public Woker(String name) { this.name = name; } //약속 부분 ! //나중에 start 메서드를 통해서 쓰레드한테 일 시작해라고 명령을 줄 수 있다. // 그러면 쓰레드는 run 메소드 안에 있는 부분을 수행 시킨다. -- 약속 !! @Override public void run() { for(int i = 0; i<50; i++) { System.out.println("worker " + name+" : " +i); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } //end of run!! }쓰레드 사용방법
누구 새로운 작업자를 생성해주는가 확인
package ch01; public class ThreadTest2 { // 메인 쓰레드 public static void main(String[] args) { // 흐름 // Run main 쓰레드시작,main쓰레드종료 // Runnable에서 쓰레드2개가 번갈아가면서 Not Runnable에 쉬었다가 작업함 // 사용하는 방법 연습 ! System.out.println("-----------main 쓰레드 시작------------"); // 현재 쓰레드가 누구야 알아 보는 명령어 !! System.out.println(Thread.currentThread()); // main,5,main 여기서 5는 우선순위 현재 쓰레드가 누구인지 확인 // 뭐부터 실행해라고 명령하는거는 개발자가 컨트롤 할 수 있는 영역이 아니다. // 작업자 하나 만들어 내기 (메인 쓰레드가 일 함) Woker woker1 = new Woker("워커1"); // 너가 위임 받은 일을 시작해 --> start(); woker1.start(); // 일해라 시켜놓고 먼저 출력값 --- main 쓰레드 종료 ----가 나온다. // 버튼을 눌러놓고 프로그램이 알아서 일을 하는거를 생각해보면 된다. // 쓰레드 (작업자) 하나 더 생성해보기 // 동시에 작업자를 만들었기 때문에 woker1이 시작되고 바로 woker2가 실행되서 같이 작업한다. Woker woker2 = new Woker("워커2"); // 멀티 쓰레드 woker2.start(); System.out.println("-----------main 쓰레드 종료------------"); } // end of main } // end of class쓰레드 예제
package ch02; // sharedResource 상황을 구현해 보자. public class BankAccount { int money = 100_000; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } // 출금,입금 기능 //synchronized <--동기화 처리진행 public synchronized void saveMoney(int money) { int currentMoney = getMoney(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } setMoney(currentMoney + money); System.out.println("입금후 계좌 잔액"+ getMoney()); } public synchronized int widthDraw(int money){ // 10만원 상태 int currentMoney = getMoney(); try { //10만원 -> 어머니가 먼저실행 95000 -> 아버지가 진행중 11만원 Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (currentMoney >= money) { setMoney(currentMoney - money); System.out.println("출금후 계좌 잔액 : " + getMoney()); return money; }else { System.out.println("계좌 잔액이 부족합니다."); return 0; } } } class Father extends Thread{ BankAccount account; public Father(BankAccount account) { this.account = account; } @Override public void run() { //입금 account.saveMoney(10_000); } } class Mother extends Thread{ BankAccount account; public Mother(BankAccount account) { this.account = account; } @Override public void run() { account.widthDraw(5_000); } }package ch02; public class MainTest1 { public static void main(String[] args) { //현재 잔액 10만원 BankAccount bankAccount = new BankAccount(); Father father = new Father(bankAccount); Mother mother = new Mother(bankAccount); //아버지 입급하기 father.start(); //3초 걸림 //어머니 출금하기 mother.start(); //정상 처리 : 10 + 1만원 입금 - 5천원 = 10만 5천원 //먼저 락을 걸어주고 작업을 진행하고 그뒤에 작업하는거 } }출력값 출금후 계좌 잔액 : 95000 입금후 계좌 잔액105000값이 원래 지정한 값에서 더하고 빼고 되지않고 정상처리 된것을 볼 수 있다.
thread의 수행순서
package ex2; public class Reray { int run = 0; public int getRun() { return run; } public void setRun(int run) { this.run = run; } public synchronized void runningman(int run) { int Reraymm = getRun(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } setRun(Reraymm + run); System.out.println("선수가 총" + getRun() + "M만큼 이어 달렸습니다"); } } class Runningman1 extends Thread { Reray reray; public Runningman1(Reray reray) { this.reray = reray; } @Override public void run() { reray.runningman(500); } } class Runningman2 extends Thread { Reray reray; public Runningman2(Reray reray) { this.reray = reray; } @Override public void run() { reray.runningman(300); } } class Runningman3 extends Thread { Reray reray; public Runningman3(Reray reray) { this.reray = reray; } @Override public void run() { reray.runningman(200); } } class Runningman4 extends Thread { Reray reray; public Runningman4(Reray reray) { this.reray = reray; } @Override public void run() { reray.runningman(800); } }package ex2; public class MainTest { public static void main(String[] args) { Reray reray = new Reray(); Runningman1 runningman1 = new Runningman1(reray); Runningman2 runningman2 = new Runningman2(reray); Runningman3 runningman3 = new Runningman3(reray); Runningman4 runningman4 = new Runningman4(reray); runningman1.start(); runningman2.start(); runningman3.start(); runningman4.start(); } }출력값 선수가 총500M만큼 이어 달렸습니다 선수가 총1300M만큼 이어 달렸습니다 선수가 총1500M만큼 이어 달렸습니다 선수가 총1800M만큼 이어 달렸습니다thread의 수행순서는 개발자가 완전히 제어할수 없는 부분이다.