< Thread 클래스 => 멀티쓰레드 > (java.lang)
기본사용법
1. Thread 클래스 상속 ( 해당 클래스에 추가적인 변수/생성자/메소드 추가가능 )
2. run 메소드 @Override 필수 ( Generator - overriding methods )
- Thread.start( ) 하면 run( ) 메소드가 실행됨
- run( )메소드에 실행하고싶은 메소드들 집어넣어서 사용.
3. Thread 상속한 자식클래스로 객체 생성 ( 이 자식객체로 Thread 기술 쓸 수 있음 )
4. Thread.start( ); (=> run 메소드 실행 )
class A extends Thread {
@Override
public void run( ){ 멀티 스레드 방식으로 실행하고자 하는 코드 + 다른 메소드들;
void M( ){...100번반복 ; } } //run 실행하면 M 도 실행
main( ){
A a1 = new A( ); // 쓰레드 상속한 A 로 객체 생성
a1.start( ); // run메소드 호출 // run 메소드 실행대기상태 //
sout("Main스레드 종료"); }
// run( ) 호출하는 시점에서cpu는 메소드 main 실행중인 상태 (running)
=> 호출된 run( ) 은 이제 호출된거니까 번호표 뽑고 들어오는거고 (runnable)
=> running상태인 main 이 먼저 실행됨 (우선순위 일뿐 사실상 동시에 처리)
(스레드 두개일 때)
A a1 = new A("one");
A a2 = new A("two");
a1.start(); //사용자 스레드 1
a2.start(); //사용자 스레드 2
System.out.println("메인종료"); // 메인 스레드 3 ( 개수 = 만든 스레드 + 1(main) )
=> running 인 main이 먼저 끝나고 (애도 멀티스레드 처리, 다만 우선순위가 높아서)
=> (runnable 상태) a1,a2 run( )메소드는 사실상 동시에 실행 됨 ( 우선순위 5로 똑같음 => 순서 랜덤 )
=> 이렇게 동시에 실행된다는 것이 "멀티스레드" 사용하는 것
( 만약 멀티스레드로 실행하지않고 메인메소드 싱글스레드로만 실행하면 =>
a1 실행, a2 실행, sout 실행 하나하나 순서대로만 실행된다. )
< Runnable 인터페이스 이용 > (java.lang)
- 클래스는 다중상속이 불가능하다.
- A 클래스가 Thread 가 아닌 다른 클래스를 extends 하고 있다면, Thread 상속이 불가능하다.
=> Interface로 돌려서 사용하는 찌까비 기술
1. Runnable 인터페이스 상속 ( implements )
2. run 메소드 오버라이딩
3. Runnable 하위클래스 객체 생성 ( 상속한 클래스로 객체 생성 )
**4. Runnable 객체를 이용해서 Thread 객체 생성 (찌까비)
5. start( ) 호출 ( *Thread 객체 사용 / NOT Runnable 객체 )
class A extends C implements Runnable {
String name;
A(String name) { this.name = name; }
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "스레드 : " + i); } }}
public class RunThread {
public static void main(String[] args) {
A a1 = new A("one");
A a2 = new A("two"); // Runnable 상속한 자식클래스로 객체 생성
Thread ta1 = new Thread(a1); // Runnable 객체 넣어서 => Thread 객체 생성 (찌까비)
Thread ta2 = new Thread(a2);
ta1.start();
ta2.start();
System.out.println("메인종료"); } }
--------
< 익명객체 사용 Runnable >
Runnable => 인터페이스 => 내부인터페이스로 익명객체로 만들어서 많이 사용 된다.
public static void main(String[] args) {
< 메소드 안의 내부 인터페이스 Runnable >
Runnable r1 = new Runnable( ) { //익명객체사용 _ 메소드 안에서
@Override
public void run() { //run( ) 오버라이딩 구현
for (int i = 0; i < 10; i++) {
System.out.println("스레드 : " + i); }
}
} ; // 익명객체 특징 => ; 로 끝낸다.
( => Runnable 인터페이스의 하위 클래스(익명A)를 이용해 객체 r1(익명객체) 생성까지 완료 )
Thread tr1 = new Thread( r1 ); // Runnable 상속한 익명객체로 Thread 객체 생성
tr1.start();
System.out.println("끝나면 메인종료");
< 쓰레드 실행 우선순위 > => setPriority( ) 메소드
getPriority ( ) => 우선순위 확인용
스레드 실행 우선순위 : 1 - 10
( 우선순위가 10이 높고 > 1은 낮다 ) 기본값 우선순위 = 모두 5 여서 랜덤으로 뽑히는 것
setPriority ( )=> 우선순위 설정하기
setPriorirty (Thread.MAX_PRIORITY); = 10 => 우선적으로 실행해라
setPriorirty (Thread.MIN_PRIORITY); = 1 => 가장 늦게 실행해라
setPriorirty (Thread.NORM_PRIORITY); =5 => 기본값으로 맞춰줘라
- 가능성의 영역이다. 예를 들어, a1 은 5분걸리고, a2는 5초 걸리는 작업이면
우선순위 a1 줬어도 a2가 먼저 실행되기 쉽다.
(심지어 같은 시간이 걸리는 작업이라도 100% 우선순위대로 실행되는건 아니다.)
< Thread 상태도 >
1. 스레드 생성상태
Thread t1 = new A( ); // 생성만 해놨어요
=>
2. t1.start( ) 호출하기
=>
3. runnable 상태 ( 대기상태 ) => 스레드 스케쥴러 동작 ( 순서 줄세우기 )
=>
4. running 상태 // cpu 가 thread 의 run 메소드 실행하고있는 상태
=>
5. 종료상태 // run 메소드 종료 ( 다 실행하고나서 그냥 알아서 종료 )
< Thread 스케쥴러 > 줄세우기
1. 우선순위 적용방식
Thread.MAX,MIN,NORM_PRIORITY
t1.setPriority(Thread.MAX_PRIORITY)
+ 기본적으로 대기시간이 길어질수록 => 우선순위 높아진다 (자동)
근데, t2 이제 대기시간도 길어지고, 우선순위 10 되가는데
앞에 t1 스레드 실행중 무한루프돌아서 안끝나버림
=> 스케쥴러 멘붕옴 (모순생김)
"아니 t2 해야되는데 t1 이 안끝나!!"
=> round-robin 방식과 섞어서 사용한다.
2. round-robin (time slice) 방식 적용
=> 스레드들마다 일정규칙으로 시간을 분배해주는 방식
( OS에 따라서 =>1번만 적용하기도 / 1+2번 적용하기도 한다.
- jdk 라이브러리 제공 X )
우선순위10 - t1, t2 < = 0.5초씩 배분
우선순위 5 - t3 < = 0.3초씩 배분
우선순위 1 - t4, t5 < = 0.1초씩 배분
=> 배분 시간안에 안끝나면 쿨하게 다음 Thread 를 실행한다 => 반복
=> 무한루프걸려도 다른 쓰레드도 실행 가능
< Thread 일시정지 메소드 >
(1) Thread.sleep(1000) (*static 메소드 - Thread 로 바로 소환 )
=> 스레드 실행 잠시 멈춤 ( 출력 속도 조절 )
millis 단위라 1000 - 1초 => 1초동안 잠시 멈춰라
- Interrupted Exceptioin 잡아서 사용해야한다
try{Thread.sleep(1000 )
catch(InterruptedException e){ }
sleep 일시중단
=> 지정시간 경과하면 다시실행
=> running 상태 X
runnable 상태 O (대기상태) => 다시 번호표 뽑고 들어온다.
=> (바로 다시 실행 보장 X)
(2) T1. join( ) => 끼워넣기
특정 메소드의 수행결과를 이용해야하는 상황 => 그 메소드 Thread를 끼워넣어서 먼저 실행시킴
=> 원래 running 상태 스레드 일시정지 => join 스레드 먼저 실행
=> join 스레드 실행완료하기까지 대기 => 완료하면 다시 runnable 상태로 ( running 상태 X )
- static 아니다. 객체생성해서 사용
Thread t1 = new A( );
t1.start( ); ==> run( ) 실행
- Interrupted Exceptioin 잡아서 사용해야한다
class A extends Thread{
public void run( ){
Thread t2 = new Thread( ); // 객체 생성 해서 사용
try{ t2.join( ); }=> t2 끼워넣기(join) => t1 일시정지 / t2 완료까지 실행 / t1 runnable상태
catch( InterruptedException e){ }
< Thread 동기화 Synchronized > => synchronized 메소드는 개인주의
=> 하나의 공유 객체를 두 개 이상의 Thread 가 사용 할 경우
공유 => 객체 내부 변수값 유지가 안된다.
t1 스레드 => 공유 객체.A = 10;
t2 스레드 => 공유 객체.A = 20;
t1.start( )
t2.start( )
=> 누가 먼저 / 누가 나중에 실행될지 정해진게 없음
=> 처음에 t1 실행 => 10
=> 나중에 t2 실행 => 20 으로 바꿔놓음 => t1 이 가졌던 Data =10 값 날라감 (유실)
=> 공유 객체에 열쇠를 를 채워놓고,
현재 객체를 사용하는 Thread 와 동기화 시킨다 ( Synchronized )
- t1 이 쓸 동안 다른애들은 접근금지
- 열쇠하나밖에 없다.
- 하나의 Thread가 시작됬으면 종료가 될 때가지 다른Thread 는 대기 시킴
- 종료되고 나면 열쇠가진 다음 Thread와 동기화
class Data {
int value;
public synchronized void saveValue(int value){
this.value = value;
try { Thread.sleep(2000); // 동시에 실행되는지 확인하기위해 잠재우기 추가
} catch (InterruptedException e) { }
System.out.println(Thread.currentThread().getName() +" : " + this.value); }}
class DataThread1 extends Thread{
Data d; // 공유 객체 Data d;
DataThread1(String name, Data d){
super(name);
this.d = d; }
@Override
public void run() {
d.saveValue(10); }} // Thread1 공유 객체에 saveValue 메소드로 10 투입
class DataThread2 extends Thread{
Data d;
DataThread2(String name, Data d){
super(name);
this.d = d; }
@Override
public void run() {
d.saveValue(20); }} // Thread2 공유 객체에 saveValue 메소드로 20 투입
public class Synchonized_Key {
public static void main(String[] args) {
Data d = new Data();
DataThread1 t1 = new DataThread1("스레드1", d);
DataThread2 t2 = new DataThread2("스레드2", d);
( 쓰레드 t1 과 쓰레드 t2 이 공유객체 d 를 같이 쓰고 있는 상황 )
t1.start();
t2.start();
=> Synchronized
(1) 객체 내부변수값 공유 X
=> Thread 들이 공유하는 Data객체의 SaveValue 메소드에 synchronized 가 걸려있다.
=> 공유 객체의 synchronized 메소드는 각 Thread 마다 그 Thread 전용으로 동기화 된다.
=> 각 Thread 별로 @Override 한 synchronized 메소드의 리턴값은 공유되지않고,
각 Thread 마다 자신의 값을 받고 종료된다.
멀티스레드 ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ ㅡ 동기화
t1.start = 20; === synchronized ===> t1.start = 10;
t2.start = 20; === synchronized ===> t2.start = 20;
(2) 동시 X
=> 원래 Thread로 run( ) 하면 (사실상)동시에 멀티쓰레드로 실행되는데 ( 대충 우선순위만 지키면서 동시에 )
synchronized 를 발견하면, Thread 들은 멈춰선다. (열쇠가 없다)
=> 대략적인 (우선순위) 규칙에 따라 동시에 실행되지않고
확실한! (key열쇠)를 가진 Thread만이 동기화되어서 하나하나 끊어서 실행된다. ( 개인주의 )
( 사실 상, Thread 를 쓰기 전, 원래 main 이 차례차례 실행 되듯이 )
멀티스레드
synchronized 메소드에 걸어둔 sleep 한번만 실행 => 값1 값2 값3 (동시에 전부 실행)
( 얘가 특이한거다 )
(값들끼리 공유가능성 O )
(즉, synchronized 메소드로 값은 3번을 뽑는데
사실상 메소드 사용은 한번밖에 하지 않는 것
메소드를 한번 사용할때 스레드 여러개가 동시에 들어가서 우르르르 동시에 실행
=> 메소드검문 sleep 에도 한번밖에 안찍힘 )
==synchronized===>
동기화
sleep 실행 => 값1 /
sleep 실행 => 값2 /
sleep 실행 => 값3 /
( sleep 매번 쓰레드가 바뀔 때마다 적용 )
( 즉, 스레드 바뀔때마다 매번 synchronized메소드 실행 )
( 이게 원래 싱글스레드로 메인 실행될때의 방식이겠지 )
t1 스레드 - 공유 상태 ( synchronized 메소드 사용중 )
Data객체.a = 10;
a 출력 => 10 // 여기서 동기화 되있을 때, 출력까지 다 해서 가져가라.
작업 끝나면
t2 스레드 - ( 대기 상태 ) => 공유 상태
Data객체.a = 20;
a 출력 => 20; // 나도 여기서 사용하고 끝. 공유X
=> 각 스레드별로 개인주의로 synchronized 메소드를 사용하게 된다.
[ synchronized ]
- 하나의 객체를 공유해서 사용해야할 때 사용!
( 내부변수값 공유 X 각자 사용 )
- 메소드 정의할때 붙인다! ( 공유 class 안의 run( )에 의해 실행되는 메소드 ) ( modifier 처럼 )
- synchronized 가 붙은 메소드에는
Thread가 동시에 실행되지않음! 매번 하나씩만 실행
매 Thread 실행마다 새롭게 구별 되어 사용 됨
<(참고) Thread 이름주기 >
1. 직접 생성자로 만들기
class A implements Runnable {
String name;
A(String name) {
this.name = name;}
@Override
public void run() {
System.out.println( "스레드 이름 : " + name );}
public class ThreadName {
public static void main(String[] args) {
A a1 = new A("one");
A a2 = new A("two");
2. getName( ) 메소드 사용
Thread 클래스는 getName( ) 메소드를 가지고 있다.
( => 몇번 스레드인지 간단히 표시 thread0 thread1 )
3. 생성자에서 super 사용
class A extends Thread{
A(Stirng name){ super(String name };
} 이름 넣어놔두고
getName( ) 메소드 사용하면 넣어둔 이름으로 나온다.
혹은 실행메소드에서 setName으로 이름다시 주면 다시 설정된다.
4. currentThread( ) 메소드 사용
Thread.currentThread( ) 현재 실행하고 있는 스레드
Thread.currentThread( ).getName( ) 이렇게 이름 뽑는다.
'Back to the Java' 카테고리의 다른 글
Generic < T > 제너릭 (0) | 2022.07.07 |
---|---|
Collection Framework => ArrayList / HashSet / HashMap (0) | 2022.07.07 |
날짜 시간 / Date => Calendar + SimpleDateFormat => LocalDate (0) | 2022.07.04 |
DecimalFormat 출력 포맷 맞추기 (0) | 2022.07.04 |
Wrapper 클래스 (0) | 2022.07.04 |