Thread(스레드) : 자바로 멀티태스킹 하는 것을 가능하게 만드는 객체
스레드 => 프로세스 내부에서 실행되는 명령 모임
CPU가 지시해서 스레드 하나하나마다 작업을 시킴. 스레드들이 작업을 할땐 절대 중복되지 않음
V3 바이러스검사시(이게 하나의 프로세스) 섹터를 한번에 쭉 검사를 하게되면 너무 오래걸림.
논리적으로 섹터를 구분하여 동시에 검사함. -> 이때 사용되는 개념이 스레드
다중스레드 동시에 명령이 실행가능. 스레드마다 번갈아서 빠르게 실행되서 동시실행으로 보임
클래스는 다중상속이 안됨. ClassA extends ClassB,Thread <- 불가능! 이럴땐 Runnable 인터페이스 이용
스레드가 움직이는 기본적인 구조
class MyThread extends Thread {// Thread클래스 상속받아서 사용 가능
private int num;
private String name;
public MyThread(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public void run() {// Thread클래스. 다양한 메소드 존재. 가장 중요한 건 run!. 스레드 사용시 반드시 런을 정의해줘야 함
int i = 0;
while (i < num) {
System.out.println(this.getName() + ":" + name + i);
i++;
// run() 안에는 무조건 try안에 sleep이 있어야 함!
try {
sleep(100); // 100-> 0.1초, 1000->1초. 메소드가 0.1초 만큼 쉬는 시간을 줌
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
public class Test1 {//t1과 t2는 별개의 프로그램
public static void main(String[] args) {
System.out.println("main 시작......");
MyThread t1 = new MyThread(100, "첫번째 : "); //t1이 쉬는 sleep 0.1초 찰나의 시간에 t2가 올라감. 그래서 전체적인 흐름이 번갈아서 출력이 되는 것
MyThread t2 = new MyThread(200, "두번째 : ");
t1.start();//스레드호출메소드(run()메소드)
t2.start();
//스레드 3개가 실행된 것 (main, t1, t2)
System.out.println("main 종료......");
}
}
Runnable 인터페이스를 이용한 스레드
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.sleep(100);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println("main 시작....");
Thread t1 = new Thread(new MyThread2(100, "첫번째"));
Thread t2 = new Thread(new MyThread2(200, "두번째"));
//실행순서는 CPU가 조정하여 정확하게 일치시킬 순 없음
t1.start();
t2.start();
//스레드 3개가 실행된 것 (main, t1, t2)
System.out.println("main 종료....");//메인스레드가 종료되도 t1,t2는 계속 진행
}
}
캘린더를 활용하여 스레드 사용하기
import java.util.Calendar;
class TClock implements Runnable {
private int num;
public TClock(int num) {
this.num = num;
}
@Override
public void run() {
int i = 0;
while (i < num) {
System.out.printf("%1$tF %1$tT\n", Calendar.getInstance());//동일한 매개변수 사용시 1$
i++;
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
/*
//무한루프로 돌려도 상관없음
while(true){
System.out.printf("%1$tF %1$tT\n", Calendar.getInstance());
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
*/
}
}
public class Test3 {
public static void main(String[] args) {//main 스레드
System.out.println("main 시작....");
Thread t1 = new Thread(new TClock(100));
t1.start();
}
}
스레드 우선순위 : 1이 가장 낮고 10이 가장 높다
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("NORM: "+ Thread.NORM_PRIORITY); //5
System.out.println("MAX: "+ Thread.MAX_PRIORITY); //10
//스레드 기본 우선순위 : 모든 스레드는 기본 우선순위를 5로 가짐
System.out.println(ob1.getPriority());//5
System.out.println(ob2.getPriority());//5
System.out.println(ob3.getPriority());//5
//우선순위를 변경
ob1.setPriority(Thread.MIN_PRIORITY);//1. 바로 숫자 2를 입력해도 됨
ob2.setPriority(Thread.NORM_PRIORITY);//5.
ob3.setPriority(Thread.MAX_PRIORITY);//10.
ob1.start();
ob2.start();
ob3.start();
}
}
Daemon 스레드
다른스레드에 도움을 주는 스레드로 다른 스레드가 종료되면 데몬스레드가 종료되지 않아도 프로세스가 종료된다.
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) {
// TODO: handle exception
}
}
}
}
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());
//데몬스레드는 main이 살아있는 만큼만 run함. 원래 스레드는 메인스레드가 종료되도 계속 실행됨.
t1.setDaemon(true);
t2.setDaemon(true);
t3.setDaemon(true);
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(1000);//메인이 2초 쉼
} catch (Exception e) {
// TODO: handle exception
}
try {
t1.join();//main에게 t1이 종료할 때까지 기다리라는 명령어. Waits for this thread to die.
t2.join();
t3.join();
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("main 종료...");//join이 없을때, main스레드가 끝나면 데몬스레드도 같이 종료
}
}
스레드 생명주기(Time to live)
class MyThread6 extends Thread{
@Override
public void run() {
try {
System.out.println("스레드 시작..");
System.out.println("우선순위: " + getPriority());
System.out.println("스레드 이름: " + getName());//첫번째 만들어지는 스레드의 내부적인 이름 : Thread-0
sleep(500);//0.5초 쉼
//우선순위 변경
setPriority(2);
System.out.println("변경된 우선순위: " + getPriority());
System.out.println("스레드 종료..");
} catch (Exception e) {
// TODO: handle exception
}
}
}
public class Test6 {
public static void main(String[] args){
Thread t1 = Thread.currentThread();//현재 스레드(=main 스레드)를 t1에 할당.
Thread t2 = new MyThread6();//upcast. MyThread6가 가지고 있는 메소드중 부모클래스가 없는게 있다면 upcast 안됨
System.out.println("main스레드 우선순위: " + t1.getPriority());//모든 스레드는 디폴트가 5
System.out.println("main스레드 이름: "+ t1.getName());//main
System.out.println("start()메소드 호출 전의 isAlive: " + t2.isAlive());//start이전이므로 당연히 false
t2.start();
//t2의 우선순위
System.out.println("t2의 우선순위: " + t2.getPriority());
//t2의 우선순위 변경(5->1)
t2.setPriority(1);
try {
//0.1초 쉼
Thread.sleep(100);
//t2종료확인
System.out.println("t2 살아있냐? " + t2.isAlive());
//1초 쉼
Thread.sleep(1000);
//t2종료확인
System.out.println("1초후 t2 살아있냐? " + t2.isAlive());
t2.join();//main스레드 wait요청. 이미 t2가 종료되어있으므로 join해도 종료되어있음. 한번 종료된 스레드는 다시 살릴 수 없음
System.out.println("t2 그래도 살아있냐? " + t2.isAlive());
} catch (Exception e) {
// TODO: handle exception
}
}
}
인터럽트
우선순위가 높은 프로그램을 먼저 실행시키고 다시 돌아온다. 꼬리를 물면서 실행시키는 방법.
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);//2초쉼
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(getName()+":"+i);
if(next.isAlive())//스레드가 살아있으면 멈춰라
next.interrupt();//다음번 스레드를 살려내는 역할을 하는게 인터럽트
}
}
}
public class Test7 {
public static void main(String[] args) {
MyThread7 t1 = new MyThread7();
MyThread7 t2 = new MyThread7();
MyThread7 t3 = new MyThread7();
t1.setNext(t2);//t1이 멈추면 t2이 살아남
t2.setNext(t3);//t2이 멈추면 t3이 살아남
t3.setNext(t1);//t3이 멈추면 t1이 살아남
t1.start();
t2.start();
t3.start();
t1.interrupt();
}
}
동기화 블록
class MyThread8 implements Runnable{
private int bank = 10000;
private int getBank(){//잔액확인메소드
return bank;
}
private int drawMoney(int m){//잔액인출메소드
bank -=m;//bank=bank-m;
return m;
}
@Override
public void run() {
int money_need = 6000;//인출금액
int money;//잔액
String msg = "";
try {
//동기화블럭 t1 스레드가 들어오게되면 다른 스레드 절대 접근불가
synchronized (this) {
if (getBank() > money_need) {//잔고가 더 크면 진행
Thread.yield();
// A hint to the scheduler that the current thread is willing to yield its current use of a processor.
// The scheduler is free to ignore this hint.
// sleep 메소드처럼 다른 스레드에게 실행할 기회를 주기 위해 사용됨.
// 우선권이 동일한 스레드에게 실행 기회 제공
money = drawMoney(money_need);
} else {
money = 0;
msg = ", 인출실패";
}
}
System.out.println(Thread.currentThread().getName()+ msg +", 인출금액: " + money + ", 잔고: "+ getBank());
} catch (Exception e) {
// TODO: handle exception
}
}
}
public class Test8 {
public static void main(String[] args) {
MyThread8 ob = new MyThread8();
//Thread t1 = new Thread(new MyThread8()); 동일
Thread t1 = new Thread(ob);
Thread t2 = new Thread(ob);
t1.start();
t2.start();
//스레드 t1,t2가 run을 점유하려고 진행하다보니 잔고가 -2000, -2000이 되는 경우 발생
//동기화 블록으로 해결 synchronized(){}
//wait() 메소드, notify() 메소드 는 동기화 블록에서만 사용
}
}
wait() 메소드, notify() 메소드 는 동기화 블록 synchronized(){} 에서만 사용
class MyThread9 implements Runnable{
private int bank = 10000;
private int getBank(){
return bank;
}
private int drawMoney(int m){
if(getBank()>0){
bank -= m ;
System.out.println(Thread.currentThread().getName() + ",인출: " +m +", 잔액:" + bank);
}else{
m=0;
System.out.println(Thread.currentThread().getName() + "잔액부족!");
}
return m;
}
@Override
public void run() {
synchronized (this) {// 클래스 자체가 들어와야함. 클래스 자체가 스레드.
for (int i = 0; i <= 10; i++) {
if (getBank() <= 0) {
this.notifyAll();// 대기상태의 모든스레드를 시작
break;
}
drawMoney(1000);
if (getBank() == 2000 || getBank() == 4000 || getBank() == 6000|| getBank() == 8000) {
try {
wait();//stop
// synchronized은 원래 다른 스레드가 들어올 수 없지만 wait가 있으면 가능.
// 하나의 스레드가 사용중이면 다른 스레드는 동기화 블럭에 들어올 수 없지만 wait()가 있으면 가능하다.
} catch (Exception e) {
}
} else {
notify();
}
}
}
}
}
public class Test9 {
public static void main(String[] args) {
MyThread9 ob = new MyThread9();
Thread t1 = new Thread(ob);
Thread t2 = new Thread(ob);
t1.start();
t2.start();
}
}
정해진 시간마다 특정 작업을 하고 싶을 때
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class Test10 extends Thread{
private int num = 10;
public Test10(){
TimerTask task = new TimerTask() {//익명의클래스
@Override
public void run() {
//반복 실행할 작업
num = 0;
}
};
Timer t = new Timer();
Calendar d = Calendar.getInstance();
/*
매일 0시 0분 0초부터 하루에 한번씩 반복
d.add(Calendar.Date,1);
d.set(Calendar.Hour,0); //오후 1시는 13으로 표현. 0은 밤 12시. 1은 새벽 1시
d.set(Calendar.MINUTE,0); //분
d.set(Calendar.SECOND,0); //초
d.set(Calendar.MILLISECOND,0); //밀리세컨
t.schedule(task, d.getTime(),1000*60*60*24)//밀리세컨*초*분*시
*/
t.schedule(task, d.getTime(), 5000);// 5000:interval(5초간격)으로 실행
}
@Override
public void run() {
while(true){
System.out.println(num++);
try {
sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args) {
//Test10 ob = new Test10();
//ob.start();
new Test10().start();//객체를 만들어서 한번만 실행하고 끝나면 됨. 메모리의 낭비를 줄일 수 있음.
}
}
스레드 그룹
스레드를 여러개 만들어서 사용할 때 그룹지어 사용
public class Test11 {
public static void main(String[] args) {
System.out.println("메인 스레드 그룹 : " +Thread.currentThread().getThreadGroup());//main이라는 그룹이 있는데 그안에 스레드가 있는 것.
System.out.println("메인 : " + Thread.currentThread());//메인스레드그룹이 들어있는 메인스레드의 정보..?
Thread t1 = new Thread();
System.out.println("t1 스레드 그룹 : " +Thread.currentThread().getThreadGroup());//t1스레드를 만들면 항상 메인스레드 그룹에 들어감
System.out.println("t1 : " +t1);
//Thread[Thread-0,5,main] = 스레드이름 Thread-0, 우선순위 5, main 스레드그룹에 들어있다
ThreadGroup tg = new ThreadGroup("tg");//그룹이름
Thread t2 = new Thread(tg, "t2");
Thread t3 = new Thread(tg, "t3");
System.out.println("t2 : " +t2);
System.out.println("t3 : " +t3);
}
}
랜덤으로 발표자 선정하기 + 스레드 활용하여 발표 결과 출력
import java.util.Random;
import java.util.Scanner;
class MyThread12 extends Thread {
@Override
public void run() {
for(int i=0;i<15;i++){
System.out.print(".");
try {
sleep(200);
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
}
public class Test12 {
public static void main(String[] args) throws InterruptedException {
Scanner sc = new Scanner(System.in);
Random rd = new Random();
MyThread12 t12 = new MyThread12();
String name[] = {"오길동", "이길동", "정길동", "박길동", "남길동", "김길동",
"강길동", "정길동", "전길동", "나길동", "장길동", "간길동", "최길동", "황길동"};
String presenter[];//발표자 몇명일지 모름. 객체 생성만.
int num, su, checkName;
do{
System.out.print("발표자 인원수를 입력해주세요[1~18]:");
num = sc.nextInt();
}while(num<1 || num>=18);
presenter = new String[num];//배열크기 지정
su=0;
while(su<num){
checkName = rd.nextInt(18);
presenter[su] = name[checkName];
for(int i=0;i<su;i++){
if(presenter[i].equals(presenter[su])){
su--;
break;
}
}
su++;
}
System.out.print("고민중");
t12.start();
try {
t12.join();//main절이 끝나지 않도록 기다려라.
} catch (Exception e) {
System.out.println(e.toString());
}
System.out.println();
System.out.println("\n***축하합니다! 발표자 입니다***");
System.out.println("* *");
int k=1;
for(String i:presenter){
System.out.printf("*%6d번 발표자 : %s *\n" ,k, i);
k++;
Thread.sleep(50);
}
System.out.println("* *");
System.out.println("*******************************");
sc.close();
}
}
'Dev > Java' 카테고리의 다른 글
[java] 객체의 직렬화, 역직렬화 (0) | 2019.01.31 |
---|---|
[java] Stream (0) | 2019.01.30 |
[java] ArrayList, List, Map, Generic, Exception (0) | 2019.01.27 |
[java] 내부클래스, 익명의클래스(Annonymous), Vector (0) | 2019.01.27 |
[java] 추상클래스, 인터페이스 (0) | 2019.01.25 |