
: 프로세스 내부에서 실행되는 명령 모임
(ex. v3백신 : section 구역마다 스레드를 설치해서 부분 검사를 통해 백신 잡아냄(바이러스 찾는 효과 빠름,효율성 ↑)
(ex. 카카오톡 : 내가 타이핑 치고 있는 동안 상대편의 대화(데이터)를 받는 작업)단일스레드 : main 메소드만을 이용하여 프로그램을 작성
main 메소드가 종료되면 프로그램도 종료된다.
명령이 하나의 흐름으로만 구성 : 동시에 명령을 실행할 수 없다.
----------------------------------------------------------------------------------------------------
다중스레드 : main 메소드뿐만 아니라 다른 메소드에서도 동시에 명령이 실행
실시간으로 데이터를 보여주는 프로그램을 만들때 사용
게임, 채팅, 주식프로그램 등
모든 스레드가 종료되어야지만 프로그램도 종료된다.
다중스레드 프로그램 작성 방법 (1)
java.lang 패키지의 [Thread 클래스]를 사용하는 방법
1) Thread 클래스를 상속 받는 자식클래스를 작성한다.
2) Thread 클래스에 선언된 run() 메소드를 오버라이딩 한다.
=> main() 메소드와 동시에 실행될 수 있는 명령을 작성
=> main 스레드와 별개로 실행되는 스레드를 작성
3) 자식클래스를 이용하여 객체를 생성한다. => 스레드 생성
4) 객체를 이용하여 start()메소드를 호출한다. => run() 메소드가 호출되면서 스레드가 실행
※ run()메소드로 호출하면 일반 메소드 호출과 동일하다.
다중스레드 프로그램 작성 방법 (2)
java.lang 패키지의 [Runnable 인터페이스]를 사용하는 방법
exception을 상속받은 동시에 runnable 메서드 = implements 인터페이스 구현 할 수 있음
java에서는 다중상속을 지원하지 않으므로 Thread 클래스를 상속받지 못하는 경우가 있다.
이럴 경우에는 Runnable 인터페이스를 상속 받아 사용한다.
1) Runnable 인터페이스를 상속 받는 자식클래스를 작성한다.
2) Runnable 인터페이스에 선언된 run() 메소드를 오버라이딩 한다.
3) 자식클래스를 이용하여 객체를 생성한다.
4) 자식클래스로 만든 객체를 활용하여 Thread 객체를 생성한다. => Thread 생성
5) Thread 객체를 이용하여 start()메소드를 호출한다.
- Thread 실행 우선순서 지정
=> Thread객체.setPriority(int newPRIORITY)메소드를 이용- Thread 제어
=> **start() 메소드 : Thread 실행
=> stop() 메소드 : Thread 중지
=> destroy() 메소드 : Thread 소멸
=> **sleep() 메소드 : 1/1000초 단위로 실행속도를 조절할 수 있다.
=> suspend() 메소드 : 스레드을 일시 정지 - wait() 메소드
=> resume() 메소드 : suspend() 메소드로 중지된 스레드를 다시 실행한다. - notify() 메소드
● 스레드 동기화
다중스레드 프로그램의 문제점
=> 스레드는 독립적으로 명령을 실행하는데 이 때 각각의 스레드가 동시에 공유된 데이타에 접근할
경우 데이타 처리에 오작동이 발생할 수 있다.
동기화 => 스레드가 공유된 데이타를 처리할 경우 먼접 접근한 스레드가 다른 스레드의 접근을
차단하기 위해 Lock를 걸어주는 기능 (A와 B스레드가 같이 class안에 있을 때, -> 서로 먼저 접근하려고 싸움)
동기화 방법
1) 공유된 데이타를 처리하는 메소드를 동기화 시키는 방법
형식) synchronized 반환형 메소드명(매개변수,...)
{
공유데이타를 처리하는 명령;
}
2) 공유된 데이타를 처리하는 메소드를 호출하는 영역을 동기화 시키는 방법
형식) synchronized(객체명) => 공유된 데이타를 포함하고 있는 객체
{
객체명.메소드명(값,..);//공유된 데이타를 처리하는 메소드
}
📌 Note Code
Test1
💻 입력
class MyThread1 extends Thread{
private int num;
private String name;
//MyThread 객체 생성
public MyThread1(int num, String name) {
this.num = num;
this.name = name;
}
// 스레드 실제 작업 (main에서 두개작업 - 2번작업하겠다)
@Override
public void run() {
int i = 0;
while (i < num) {
System.out.println(this.getName() + ":" + name + i);
i++;
try {
//기울어짐 - static 메서드
sleep(1000); //1000 = 1초, 300 = 0.3초..
} catch (Exception e) {
}
}
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println("main 시작!");
MyThread1 t1 = new MyThread1(100, "첫번째");
MyThread1 t2 = new MyThread1(200, "두번째"); //run() 2 => new가 두개니까
//객체생성뿐만 아니라 위에 run 메서드 호출해줘야함
//start 명령어 - 스레드 실행 (run()메서드 호출)
t1.start();
t2.start();
//메인도 스레드니까 1개 + new 2개
System.out.println("main 종료..");
//메인 종료시에는 두개의 스레드만 움직이고 있음
}
}
main 시작!
main 종료..
Thread-0:첫번째0
Thread-1:두번째0
Thread-0:첫번째1
Thread-1:두번째1
Thread-0:첫번째2
Thread-1:두번째2
.
.
.
📌 Note Code
Test2
💻 입력
class MyThread2 implements Runnable{
private int num;
private String name;
public MyThread2(int num,String name) {
this.num = num;
this.name = name;
}
@Override
public void run() {
int i = 0;
while(i<num) {
System.out.println(name + ":"+i);
i++;
try {
//Thread를 쓰는 이유 : Thread클래스에 있는 sleep메서드를 호출하기위해
//현재 실행 중인 스레드를 0.1초 동안 일시적으로 정지시키는 역할
Thread.sleep(100); //인터벌을 준다고 표현함
} catch (Exception e) {
e.printStackTrace(); // 에러의 원인과 발생위치 확인
}
}
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println("main시작!");
// MyThread2는 자체적으로 객체를 생성하지 않음
// 객체 생성 - (오버로딩으로 매개변수만 다름)
// MyThread2 클래스의 인스턴스를 생성하고, 이를 Thread 클래스의 생성자에 전달 -> 스레드 생성
// Thread 클래스는 생성자에 Runnable을 구현한 객체(MyThread2)를 받아 스레드를 생성할 수 있음
//t1,t2 = 스레드
Thread t1 = new Thread(new MyThread2(100, "첫번째"));
Thread t2 = new Thread(new MyThread2(200, "두번째"));
t1.start();
t2.start();
System.out.println("main종료,,");
}
}
main시작!
main종료,,
첫번째:0
두번째:0
첫번째:1
두번째:1
첫번째:2
두번째:2
두번째:3
첫번째:3
두번째:4
첫번째:4
두번째:5
첫번째:5
.
.
.
📌 Note Code
1$ = 첫번째 인수(Calendar.getInstance())
Calendar.getInstance() = 현재의 날짜와 시간 정보 반환
Calendar now = Calendar.getInstance(); //객체생성해서 현재시간 가져옴
System.out.printf("%tF %tT", now,now); //%tF = 날짜
(= System.out.printf("%1$tF %1$tT", Calendar.getInstance());)
Test3
💻 입력
import java.util.Calendar;
class MyThread3 implements Runnable {
@Override
public void run() {
while (true) {//끝 시간 정해지지않고 계속 쭉
System.out.printf("%1$tF %1$tT\n", Calendar.getInstance());
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class Test3 {
public static void main(String[] args) {
Thread t = new Thread(new MyThread3());
t.run();
}
}
2023-12-06 20:38:09
2023-12-06 20:38:10
2023-12-06 20:38:11
.
.
.
📌 Note Code
Test4
💻 입력
class MyThread4 extends Thread{
private String name;
public MyThread4(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
System.out.println(name + ":" + i);
}
}
}
public class Test4 {
public static void main(String[] args) {
MyThread4 ob1 = new MyThread4("A");
MyThread4 ob2 = new MyThread4("B");
MyThread4 ob3 = new MyThread4("C");
System.out.println("MIN: "+Thread.MIN_PRIORITY);//1
System.out.println("NOR: "+Thread.NORM_PRIORITY);//5
System.out.println("MAX: "+Thread.MAX_PRIORITY);//10
System.out.println(ob1.getPriority());//5
System.out.println(ob2.getPriority());//5
System.out.println(ob3.getPriority());//5
ob1.setPriority(Thread.MAX_PRIORITY); //우선순위 1등
ob2.setPriority(Thread.NORM_PRIORITY);
ob3.setPriority(Thread.MIN_PRIORITY);
ob1.start();
ob2.start();
ob3.start();
}
}
MIN: 1
NOR: 5
MAX: 10
5
5
5
B:1
C:1
C:2
C:3
C:4
C:5
C:6
A:1
A:2
A:3
A:4
A:5
A:6
A:7
A:8
C:7
B:2
.
.
.
📌 Note Code
Test5
💻 입력
class MyThread5 implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
}
}
public class Test5 {
public static void main(String[] args) {
System.out.println("main시작!");
Thread t1 = new Thread(new MyThread5());
Thread t2 = new Thread(new MyThread5());
Thread t3 = new Thread(new MyThread5());
//메인이 끝나면 스레드도 끝이남
t1.setDaemon(true);
t2.setDaemon(true);
t3.setDaemon(true);
t1.start();
t2.start();
t3.start();
//sleep은 꼭 try-catch안에 넣어야함
try {
Thread.sleep(1000);
}catch (Exception e) {
}
//join = 스레드가 끝날때까지 main종료 기다려
try {
t1.join();
t2.join();
t3.join();
} catch (Exception e) {
}
System.out.println("main종료...");
}
/*일반스레드
main시작!
1
main종료...
1
찰나에 1을 찍음
*/
}
main시작!
1
1
1
2
2
2
3
.
.
main종료...
📌 Note Code
Test6
💻 입력
class MyThread6 extends Thread{
@Override
public void run() {
try {
System.out.println("스레드 시작");
System.out.println("우선순위 : " + getPriority());
System.out.println("스레드이름 : " + getName()); // 스레드가 가지고있는 메서드
sleep(500);
setPriority(2);
System.out.println("변경된 우선순위 : "+getPriority());
System.out.println("스레드 종료,,,");
} catch (Exception e) {
}
}
}
public class Test6 {
public static void main(String[] args) {
Thread t1 = Thread.currentThread(); //main스레드 = currentThread()
Thread t2 = new MyThread6();
System.out.println("메인스레드 우선순위 : "+t1.getPriority()); //기본 default getPriority() : 5
System.out.println("메인스레드 이름 : "+t1.getName());
System.out.println("t2스레드 시작전 상태 : "+t2.isAlive()); //t2 살아있는 상태인지 확인
t2.start(); //t2는 여기서 실행
System.out.println("t2 우선순위 : "+t2.getPriority());//쉬는동안 line 44찍고 다시 내려오는거니까 = 1
t2.setPriority(1);
try {
Thread.sleep(100);//main
System.out.println("t2 살아있는 상태야? "+ t2.isAlive());
Thread.sleep(1000);//main
System.out.println("1초 후) t2 살아있는 상태야? "+ t2.isAlive());
t2.join(); //t2가 종료될때까지 main 기다려
System.out.println("t2 살아있는 상태야? "+ t2.isAlive());
} catch (Exception e) {
}
}
}
메인스레드 우선순위 : 5
메인스레드 이름 : main
t2스레드 시작전 상태 : false
t2 우선순위 : 5
스레드 시작
우선순위 : 1
스레드이름 : Thread-0
t2 살아있는 상태야? true
변경된 우선순위 : 2
스레드 종료,,,
1초 후) t2 살아있는 상태야? false
t2 살아있는 상태야? false
📌 Note Code
Test7
💻 입력
class MyThread7 extends Thread{
private Thread next ; //다음번으로 돌아감
public void setNext(Thread next) {
this.next = next;
}
@Override
public void run() {
for(int i=1;i<=20;i++) {
try {
sleep(2000);
} catch (Exception e) {
}
System.out.println(getName() + " : " + i);
if (next.isAlive()) {
next.interrupt();// next.isAlive()가 살아있다면 중지해라 -> 그 다음 스레드 시작
}
}
}
}
public class Test7 {
public static void main(String[] args) {
System.out.println("main 시작!");
MyThread7 t1 = new MyThread7();
MyThread7 t2 = new MyThread7();
MyThread7 t3 = new MyThread7();
t1.setNext(t2);
t2.setNext(t3);
t3.setNext(t1);
t1.interrupt(); //t1이 멈추기전 딱! 시작점
t1.start();
t2.start();
t3.start();
try {
t1.join();
t2.join();
t3.join();
} catch (Exception e) {
}
System.out.println("main 종료,,,");
}
}
main 시작!
Thread-0 : 1
Thread-1 : 1
Thread-2 : 1
Thread-0 : 2
Thread-1 : 2
Thread-2 : 2
Thread-0 : 3
Thread-1 : 3
Thread-2 : 3
.
.
Thread-0 : 20
Thread-1 : 20
Thread-2 : 20
main 종료,,,
📌 Note Code
Test8
💻 입력
//동기화 : 하나의 데이터를 여러개로 나누었을 때
//간단한 은행프로그램
class MyThread8 implements Runnable{
private int bank =10000;
//잔액
private int getBank() {
return bank; ///은행 잔액 나한테 보여줘
}
//인출
private int drawMoney(int m) {//얼마나 인출할지 (int m)
bank-=m;//bank = bank - m
return m; //인출잔액
}
@Override
public void run() {
int moneyNeed = 6000; // 인출금액
int money;
String msg = "";
try {
//동기화블럭으로 막아줌 (마이너스 안나옴)
synchronized (this) { //**synchronized 마이스레드
if (getBank() >= moneyNeed) {
money = drawMoney(moneyNeed);
msg = "인출 성공!";
} else {
money = 0;
msg = "인출 실패!";
}
}
System.out.println(Thread.currentThread().getName() + msg + ",인출금액 : " + money + ", 잔고 : " + getBank());
} catch (Exception e) {
}
}
}
public class Test8 {
public static void main(String[] args) {
MyThread8 ob = new MyThread8();
Thread ob1 = new Thread(ob);
Thread ob2 = new Thread(ob);
//Thread ob1 = new Thread(new MyThread8()); //A은행
//Thread ob2 = new Thread(new MyThread8()); //B은행
ob1.start();
ob2.start();
}
}
Thread-0인출 성공!,인출금액 : 6000, 잔고 : 4000
Thread-1인출 실패!,인출금액 : 0, 잔고 : 4000
📌 Note Code
TimerTask(추상클래스) : 정해진 시간에 특정 작업을 반복하고 싶을 때 / 특정 포인트마다 반복
-> new를 쓰면 반드시 가지고있는거 하나는 Override 해야함
이때의 run()메서드는 스레드의 메서드와는 별개임
여기서 다른 스레드 작업과는 다른점 :
new Test9().start(); = 메모리에 Test9 자체가 올라가서 (자생하게끔) 너가 가지고 있는 메서드 계속 실행해도 좋다는 뜻
Why? Test9 ob = new Test9;하면 ob는 한번만 실행하므로, Test9에 다른 메서드가 있다면 이렇게 객체생성해야하는게 맞음. 그러나 ob는 객체생성하고 나면 한번만 쓰이는데 TimerTask는 반복해서 쓰여야하므로 new Test9().start(); 이렇게 메모리에 올려야함
Test9
💻 입력
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
//task
//정해진시간에 특정작업을 반복하고싶을 때
public class Test9 extends Thread { //자기 자신 = 스레드
private int num = 0;
public Test9() {
//TimerTask 추상클래스 - new써주면 추상클래스가 가지고있는거 반드시 오버라이드 하나는 해야함
TimerTask task = new TimerTask() {
@Override
public void run() { //별개 클래스의 run
num =1; //반복적인 작업
}
};
Timer t = new Timer();
Calendar d = Calendar.getInstance(); //정해진 시간
/*
내일 0시0분0초부터 하루에 한번씩 반복
d.add(Calendar.DATE,1); //1일후
d.set(Calendar.HOUR,0); //시간 = 0 , 오후1시 : 13
d.set(Calendar.MINUTES,0); //분 = 0
d.set(Calendar.SECOND,0); //초 = 0
d.set(Calendar.MILLISECOND,0);
t.schedule(task,d.getTime(),1000*60*60*24); //밀리,초,분,시 =>1일을 나타냄
*/
t.schedule(task,d.getTime(),5000); //시작한 시간(d.getTime)부터 5초마다 num=1;로 초기화시키는 작업(task)실행해
}
@Override
public void run() { //스레드의 run
while(true) {
System.out.println(num++); // 이 run은 스레드꺼라서 계속 실행되는건데, num -> 위에 run에서 5초마다 초기화 task
try {
sleep(1000);
} catch (Exception e) {
}
}
}
public static void main(String[] args) {
//Test9 ob = new Test9();
//ob의 값을 test9에 한번 넣고 = 시작하고나서는(ob.start()) 다시 ob값을 쓰지 않음
//만일 test9에 다른 메서드가 있다면 이렇게 실행해야함
//ob.start(); //ob에 있는 스레드 실행하고싶을 떄
//메모리에 넣어둬서 자생하게끔
new Test9().start(); //**메모리올라가.니가가지고있는 메서드 실행해. (점 중요) - ob가 한번 시작하고나서는 다시 쓰지않아서 ob안쓰니까
}
}
1
2
3
4
5
1
2
3
4
5
.
.
.