1. 스레드 들어가기 전
2. 하드디스크의 구조
붉은 우즈마키 : 섹터 (나이테로 비유해서 이해함)
막 일케 고속으로 회전하면서 데이터를 찾으면 메모리에 올려보냄. 하드디스크는 램처럼 전류로 움직이는게 아니기 때문에 램보다 당연히 느림. 물리적으로 그으면서 (판에다가 상처내면서) 기록하고 그 그은걸 기반으로 데이터 찾는다.
파랑 선 : 액츄에이터 핀 (하드디스크의 동작을 제어하고 디스크 헤드를 움직이는 역할)
액츄에이터 핀은 컴퓨터가 보내는 신호를 받아 디스크 헤드를 정확한 위치로 이동시킨다. 그러면 디스크 헤드는 그 위치에 있는 데이터를 읽거나 쓸 수 있게 되는 거죠. 이렇게 함으로써 우리가 원하는 데이터에 랜덤하게 접근할 수 있게 되는 것.
액츄에이터 핀은 수평이동함!
2-1. 풀스캔 시간 계산
전체 풀스캔 시간 = 나이테(섹터) 도는 시간 + 시크(Seek) 시간 + 액츄에이터 핀 움직이는 시간
2-2. 풀스캔 하지 않고 데이터 읽기
수평 이동을 한 번에 여러칸 하면 풀스캔 시간을 단축시킬 수 있다. (=안 할 수 있다.)
테이블이나 인덱스와 같은 데이터 구조를 활용하여 특정 데이터가 어느 나이테에 저장되어 있는지 미리 파악한 후, 이동 거리를 최소화하여 효율적으로 데이터에 접근하는 방식이다. > 수평 이동을 10번씩 하고 한 바퀴를 돌면 풀스캔이 완료된다고 가정해보자. 테이블 등을 활용하여 어떤 데이터를 어느 나이테에 저장해놨는지 알고 있으면 수평 이동을 10번씩 하면서 해당 데이터를 찾을 수 있다. 이렇게 하면 풀스캔 시간을 단축시킬 수 있음!
이 방법은 데이터 구조를 미리 파악하고 테이블을 사용해야 한다는 점에서
추가적인 작업과 비용이 필요할 수 있다.
3. I/O
하드디스크 입장에서 데이터를 로드하거나 저장하는 과정을
I/O (input / output) 라고 함
메모리한테 영구 저장할 데이터를 받고 (input), 그 데이터를 메모리한테 보내고 (output)
메모리 입장에서는 데이터를 로드하거나 할당하는 과정을 통해 작업을 수행
하드디스크한테서 데이터를 받아오거나 (로드), 데이터를 주거나 (할당… 세이브?)
commit / write / input = 메모리에 있는 데이터를 하드디스크에 저장하는 것
[ 하드디스크에서 데이터를 끌어올렸는데! ]
램(RAM)의 용량이 실행해야 하는 프로그램이 필요로 하는 용량보다 작을 경우, 프로그램을 실행할 수 없는 상황이 발생
램의 용량이 딱 맞을 경우 : 용량이 더 필요해서 추가로 끌어올려야 하면 스왑 발생! 스왑은 현재 사용되지 않는 메모리 공간을 비워내고, 새로운 데이터를 불러와서 사용하는 방식 그런데 당장 안 쓰는 공간이 없다?! > 스왑 안통함 > 프로그램이 뻗는거임.. 이걸 막기위해! * 가상메모리 *를 만들어서 사용한다!!
[ 가상 메모리 ] → 운영체제가 알아서 함
이 과정에서 스왑 영역은 하드 디스크의 일부분을 메모리인 것처럼 CPU를 속여서 사용하는 것 하지만 스왑 영역은 CPU가 접근할 수 있는 것이지 실질적으로는 하드 디스크에 위치하므로, 메모리에 비해 접근 속도가 느릴 수 있다.
내가 하드디스크에서 프로그램을 더블 클릭해서 메모리에 올린다 = 프로세스가 됨
→ 이 프로세스를 관리하는 게 프로세서 (CPU)
* CPU = 프로세서
* 스레드 =/= 프로세스 (둘은 다르다!!)
프로세스는 하나의 프로그램이 실행될 때 운영체제로부터 자원(메모리, CPU 시간 등)을 할당받아 독립적으로 실행되는 단위 스레드는 하나의 프로세스 내에서 실행되는 더 작은 실행 단위. 프로세스는 최소한 하나의 스레드를 가지고 있으며, 추가적인 스레드를 생성할 수도 있다.
* 프로세스 - 하드디스크에 기록되어 있는 어떤 프로그램이 메모리로 끌어올려진 것
(=로드 된 것)
* 모든 cpu(프로세서)는 하나의 스레드를 반드시 가지고 있음 → 모든 언어가 동일. 예외x
* 모든 프로세스를 관리하는 애 = cpu (프로세서)
1. 스레드란?
하나의 프로세스 내에서 동시에 여러 작업을 수행하기 위해 사용되는 것 즉, 스레드는 분신이자, 일꾼이다. (스레드 = 일꾼 = CPU) 내가 일을 하는 순간을 논리적으로 스레드라고 부른다. 스레드가 1개 = 1명이서 일함 = 하나의 일밖에 못함 CPU는 처음에 MAIN 스레드가 되어서 MAIN 스레드를 관리한다.
스레드는 CPU가 실행하는 작업의 단위.
스레드는 CPU의 명령어를 실행하고 연산을 수행하는 역할을 담당
CPU는 자기 자신이 스레드가 될 수 있음.
main 스레드의 생명 주기 == main 큐 ex) main 행이 10줄 = main 큐 10칸 = main 스레드 생명주기
* 스레드 생성법
1. Thread 클래스 상속하기
2. Runnable 인터페이스 구현하기 ✓
* 왜 Runnable을 더 선호하냐?
자바에서는 단일 상속만이 가능하므로,
다른 클래스를 이미 상속 받은 클래스는 스레드로 만들 수 없기 때문!
//Runnable 인터페이스로 스레드 구현
2. 멀티 스레드란?
하나의 프로세스 내에서 여러 개의 스레드(Thread)가 동시에 실행되는 프로그래밍 모델 (= 하나의 프로그램이 여러 개의 작은 작업을 동시에 처리하는 것) 일반적으로 프로그램은 한 번에 하나의 작업만 처리하는데, 멀티스레드를 사용하면 여러 작업이 동시에 실행될 수 있다!
* 메인 스레드의 생명주기 5개 (붉은색) * 서브 스레드의 생명주기 2개 (파란색) 로 이루어져 있는 스레드다. 메인과 서브 둘 다 한번에 실행할 수는 없다!
스레드를 쓰면 컴퓨터의 속도가 빨라지는게 아님. 그냥 동시에 여러가지 일을 실행하는데, 너무 빠르니까 왔다갔다하는게 아니라 동시에 작동하는것 처럼 보임. 그래서 ux가 좋아짐. (사용자 경험이 좋아짐.) * 사용자 경험 : 사용자가 제품이나 서비스를 사용하는 동안 느끼는 전반적인 경험
[멀티 스레드는 어떻게 동작하는가?]
멀티 스레드는 시간 슬라이딩 (시분할)로 동작한다. 작은 단위로 CPU 실행 시간을 분할하여 여러 작업이 동시에 실행되는 것처럼 보이게 하는 것. 그냥! 일하는 애 한명이 0.01초씩 번갈아가면서 왔다갔다하면 동시에 실행하고 있는 것처럼 느껴지잖아. 그거임! 우리 눈을 속이려고 하는 것이다. ex) 곰플레이어 2개 띄워서 보는데, 왔다갔다하는 속도가 엄청 빠르니까 사용자 눈에는 동시에 재생하는 것처럼 보임.
멀티스레드는 내가 어디까지 했는지, 어디까지 봤는지 기록해야 그다음을 이어서 할 수 있으니까 문맥을 알고, 문맥을 기록해야 한다. 즉, 컨텍스트 스위칭(Context Switching) 시간이 있어서 좀 느리다.
컨텍스트 스위칭 = CPU가 한 스레드에서 다른 스레드로 전환되는 과정
멀티스레드 환경에서는 여러 개의 스레드가 동시에 실행되기 때문에
CPU는 스레드 간에 전환을 수행해야 한다. 이때 컨텍스트 스위칭이 일어남!
멀티 스레드 - CPU의 자식.
CPU가 없으면 스레드가 만들어질 수 없음. CPU는 스레드를 무한대로 만들 수 있음.
자바에서는 운영 체제가 제공하는 스레드를 활용하여 멀티스레드 프로그래밍을 할 수 있다. 이를 위해 자바는 네이티브(하드웨어를 운영체제를 통해서 바로 작동 시키는 것) 스레드를 가져와서 쓰는데 이걸 ‘네이티브 콜 · 시스템 콜’ 이라고 한다.
네이티브 콜 · 시스템 콜 : 프로그래밍 언어에서 운영 체제의 기능을 직접 호출하는 것
3. 멀티 스레드와 I/O
* i/o (input/output)는 메모리와 하드디스크가 작업하는 것이기에 가장 느리다. * cpu는 램한테 몇번지에 있는 데이터를 하드디스크에 기록해, 하드디스크 몇 번 섹터(=나이테)에 있는 데이터를 메모리로 들고와! 라고 명령만 하는 것.
[I/O를 한다 = CPU가 멍때린다.]
1. cpu가 할 일 2. cpu가 할 일 3. I/O → cpu가 멍때리는 시간. wait 시간. 다른 작업을 수행하지 못하고 대기. 작업 지연됨. CPU는 I/O 작업을 요청하고, 해당 작업이 완료될 때까지 기다리는 역할을 함. 4. CPU가 할 일 5. CPU가 할 일 * 3번 'I/O 작업 실'을 하나 더 만들어서 멀티스레드(다른 스레드)한테 맡겨버리는게 빠르다! 멀티스레딩을 활용하면 이러한 CPU의 대기 시간을 최소화할 수 있다.
이렇게! cpu야, 멍때리고 있는 시간에 서브 스레드 파서 일하고 와^^ 하는 것. 3번 I/O (램,하드디스크)가 하는 일을 cpu(서브 스레드)한테 노는 시간에 하라고 주는 것 CPU는 멍때리는 대신 다른 작업을 처리할 수 있다.
주 스레드는 CPU의 주요 작업을 담당하며,
서브 스레드는 주로 I/O 작업이나 다른 비동기 작업을 처리하기 위해 생성 된다.
또한 I/O 작업이 완료되면 결과를 주 스레드에게 알리거나 필요한 처리를 수행한다.
4. start()로 때리는 이유
start()
메소드를 호출하여 스레드를 실행시키면 멀티스레드 실행
run()
메소드를 직접 호출하면 단일 스레드 실행5. 멀티 스레드 간단 예시
package ex16.example3; class MyRunnable2 implements Runnable { String myName; public MyRunnable2(String name) { myName = name; } public void run() { for (int i = 0; i <= 10; i++) { System.out.print(myName + i + " "); } } } public class TestThread { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable2("A")); Thread t2 = new Thread(new MyRunnable2("B")); t1.start(); t2.start(); } }
클래스의 객체를 생성자로 전달하는 경우, 별도의 생성자를 구현할 필요는 없다.
생성자는 객체를 초기화하기 위해 사용되는 특별한 메서드로서,
클래스의 인스턴스를 생성할 때 호출된다.
클래스의 생성자는 일반적으로 인스턴스 변수를 초기화하거나 필요한 설정을 수행하는 역할을 한다. 그러나 이미 생성된 객체를 생성자로 전달받는 경우,
해당 객체는 이미 생성 및 초기화된 상태이므로 별도의 생성자를 구현할 필요가 없다.
[ 해당 코드를 단일 스레드로 ] xxx
public class TestThread { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable2("A")); Thread t2 = new Thread(new MyRunnable2("B")); t1.start(); try { t1.join(); // t1 스레드의 작업이 완료될 때까지 기다립니다. } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }
join()
메소드는 현재 실행 중인 스레드가
다른 스레드의 작업이 완료될 때까지 기다리도록 하는 메소드
join()
메소드를 호출한 스레드는 대기 상태로 들어가며,
다른 스레드의 작업이 완료되고 해당 스레드가 종료될 때까지 기다린다.
>> 쓰지 마세요. 상태 변수를 변경해서 중단하세요. 스레드 중단은 상태 변수를 변경해서 중단하는 것. interrupt() 메소드 XXX
6. 카운트 다운 프로그램
package ex16.example3; import javax.swing.*; import java.awt.*; public class CountDownTest extends JFrame { private JLabel label; class MyThread extends Thread { public void run() { for (int i = 0; i <= 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } label.setText(i + ""); //1초가 지나가면 레이블의 텍스트를 변경 //스레드가 실행될 때마다 label의 텍스트를 현재 카운트다운 값으로 변경하여 화면에 표시 } } } public CountDownTest() { setTitle("카운트다운"); setSize(400,150); label = new JLabel("Start"); label.setFont(new Font("Serif", Font.BOLD, 100)); add(label); setVisible(true); (new MyThread()).start(); } public static void main(String[] args) { CountDownTest t = new CountDownTest(); } }
멀티 스레딩의 문제점
여러 스레드들이 같은 데이터를 공유하게 되면 ‘동기화’라고 하는 문제가 발생할 수 있다.
멀티 스레드로 왔다갔다 하려면 (동시에 실행되는 것처럼 보이려면) 컨텍트 스위칭이 필요
Share article