JAVA(Sol)(정리대기중..언젠가)

210423_3~4(멀티스레드)

너굴셉 2021. 4. 23. 13:14

멀티스레드

 

1. 스레드 Thread : 어떤 로직이나 함수의 실행 단위.

 

a(){ }

                              (cpu자원)

a( ) 3번 호출  |---a()---|---a()---|---a()---|   ===> 단일스레드 - 지금까지 사용법

                                                                각각의 함수가 자원을 독점

                   (cpu자원)

a( ) 3번 호출  |---a()---|

                      |---a()---|

                         |---a()---| 

                          --   동시성을 갖고 실행되는구간===> 멀티스레드(동시에 실행되는 것처럼 보임)

                                                                             자원을 공유해서 사용한다

 

멀티스레드의 장점 

[웹서버 - 하나의 클라이언트 처리시간 1초라고 가정]

  동시에 1000명이 접속했다. 1000번째 사람의 앞에 999명의 즉999초 동안 기다려야 자신의 처리가 돌아온다

  그런데, 멀티스레드가 되면, 웹서버가 자원을 돌아가면서 모든 클라이언트들에게 처리를 조금씩 돌아가면서

  하게되므로 훨씬 빠른속도로 자신의 처리를 할 수가 있다.

 

돈이 되는 프로그램은 다 멀티다. 게임서버, db서버, 웹서버, 채팅 서버 ...~


적용방법

 

 

Runnable 인터페이스 적용하거나

   |

Thread 클래스를 상속받아서  // Thread클래스는 Runnable을 구현한 구현 클래스이다.

   |

MyThread { // 다중상속으로 Thread상속을 받을수없을때 Runnable 인터페이스를 적용한다.

  ↓

run( ) { 

멀티로 구현하고자 하는 로직이나 함수를 run 메서드로 감싼다

    }

}

 

 

기억!

Runnable Thread
run() run(), start()가 있다.

결론 : 실행하려면 start() 가 포함하는 Thread 객체(인스턴스)를 만들어야한다

 

호출

MyThread mt = new MyThread ();

mt.run() ;  <=== 호출불가. run()은 구현만 할 수 있고, 호출이 불가하다. 그래도 호출하면 단일스레드로 작동됨(주의

mt.start() ; <=== start() 라는 메서드로 호출한다.

 

원리

  start( ) <==== jvm에 요청 등록 === >run() 호출

  run( ) 은 jvm에 의해서 호출되므로 callback method라고 한다.

package chapter16;

//스레드가 없는경우
class GenClass {
	private int num; //필드1
	private String name; //필드2

	public GenClass(String a, int b) { //생성자
		name = a;
		num = b;
	}

	public void start() { //메서드1
		for (int i = 0; i < num; i++) //로직
			System.out.println(name + "." + i);
	}

}

public class NonThread {

	public static void main(String[] args) {
		GenClass t1 = new GenClass("first", 5);
		GenClass t2 = new GenClass("second", 5);
		GenClass t3 = new GenClass("third", 5);

		t1.start(); //단일스레드  jvm start()와 관련x
		t2.start();
		t3.start();
	}

}
//first.0
//first.1
//first.2
//first.3
//first.4
//second.0
//second.1
//second.2
//second.3
//second.4
//third.0
//third.1
//third.2
//third.3
//third.4
package chapter16;

//멀티스레드 적용
class MyThread extends Thread { //스레드상속
	private int num;
	private String name;

	public MyThread(String a, int b) { //생성자
		name = a;
		num = b;
	}

	public void run() {   // 멀티스레드구현 run()메서드
		for (int i = 0; i < num; i++)
			System.out.println(name + ":" + i);
	}
}

public class ThreadTest1 {

	public static void main(String[] args) {
		MyThread t1 = new MyThread("first", 1000); //인스턴스1 
		MyThread t2 = new MyThread("second", 1000); //인스턴스2
		MyThread t3 = new MyThread("third", 1000); //인스턴스3

		t1.start(); // jvm start
		t2.start(); // 단 한번이라도 처리가 섞이면 멀티스레드이다
		t3.start();
	}

}
//second:0
//third:0
//first:0
//third:1
//second:1
//third:2
//first:1
//third:3
//second:2
//third:4
//....~~

 

package chapter16;

//Runnable 인터페이스를 통한 멀티스레드
class ThreadOne implements Runnable { // Runnable 인터페이스상속//ThreadOne가 Runnable을 적용한 타겟클래스이다
	private int num;
	private String name;

	public ThreadOne(String a, int b) {
		name = a;
		num = b;
	}

	public void run() { // 멀티스레드 구현
		for (int i = 0; i < num; i++)
			System.out.println(name + ":" + i);
	}
}

public class TreadTest2 {

	public static void main(String[] args) {
		Thread t1 = new Thread(new ThreadOne(("first"), 1000));
		// Thread는 Runnable을 구현한 클래스이다
      	 	// Runnable로 만들면 하위객체를 호출하지못하기때문에 형변환도해야한다
		Thread t2 = new Thread(new ThreadOne(("second"), 1000));
		Thread t3 = new Thread(new ThreadOne(("third"), 1000));
        //생성자안의 파라미터에 Runnable의 인자(상속받은 ThreadOne)를 넣어서 사용하기때문
     	// =new Tread(new 러너블타겟()) ->new 러너블타겟()이 인자를 두개갖고있기때문에()가 또들어간것



		t1.start();
		t2.start();
		t3.start();
	}

}

동시성 구간을 크게하기

앞의 사실에서 알수있듯이, 첫 스레드에서 잠시 쉬어갈때 다른 스레드가 시작된다.

그렇다면, 강제로 쉬게 하면 더 동시성 구간이 커지지 않을까?

 

그래서 sleep(ms);메서드 ms는 1/1000초 동안 쉬도록 하면 골고루 섞인다.

package chapter16;

//동기화없는 경우
class RunThread extends Thread {

	public RunThread(String name) {
		super(name); //Thread의 String을 인자로 갖는 생성자
	}

	public void run() {
		for (int i = 1; i <= 500; i++) {
			if (i % 50 == 0)
				System.out.println("Thread[" + getName() + "]:" + i);
			                                 //getName은 상속받은 Thread에서 온것

		}
	}
}

public class SchedulingTest1 {

	public static void main(String[] args) {
		Thread[] t = new RunThread[2]; //스레드 배열

		t[0] = new RunThread("ONE");
		t[1] = new RunThread("TWO");

		t[0].start();
		t[1].start();

	}

}
//Thread[TWO]:50
//Thread[TWO]:100
//Thread[TWO]:150
//Thread[ONE]:50
//Thread[ONE]:100
//Thread[TWO]:200
//Thread[ONE]:150
//Thread[ONE]:200
//Thread[TWO]:250
//Thread[ONE]:250
//Thread[TWO]:300
//...~ 동시성이거의없다
package chapter16;

//동기화를 처리한경우
class RunThread2 extends Thread {
	public RunThread2(String name) {
		super(name);
	}

	public void run() {
		for (int i = 1; i <= 500; i++) {
			if (i % 50 == 0) {
				System.out.println("Thread[" + getName() + "] :" + i);
				try {
					sleep(1); // 1/1000초에 한번 쉼 //동시성을 크게하기위함
					System.out.println("");
				} catch (Exception e) {
				}
			}
		}
	}
}

public class SchedulingTest2 {

	public static void main(String[] args) {
		Thread[] t = new RunThread2[3];

		t[0] = new RunThread2("☆");
		t[1] = new RunThread2("★");
		t[2] = new RunThread2("◆");

		t[0].start();
		t[1].start();
		t[2].start();
	}

}
//Thread[★] :50
//Thread[☆] :50
//Thread[◆] :50
//				//sleep발생구간
//
//Thread[☆] :100
//
//Thread[★] :100
//Thread[◆] :100
//Thread[★] :150
//
//
//Thread[◆] :150
//Thread[☆] :150
//
//
//
//Thread[◆] :200
//Thread[★] :200
//Thread[☆] :200
//
//
//Thread[★] :250
//
//Thread[◆] :250
//Thread[☆] :250
//...~