// 싱글스레드 : 스레드가 하나뿐인 프로그램
public class T01_TreadTest {
public static void main(String[] args) {
for(int i =1; i<=200; i++) {
System.out.print("*");
}
System.out.println();
for(int i =1; i<=200; i++) {
System.out.print("$");
}
}
}
스레드가 하나이므로 순차적으로 실행된다.
멀티스레드를 만드는 세가지 방법!
// 멀티스레드 : 스레드가 2개이상
public class T02_TreadTest {
public static void main(String[] args) {
//방법1 : Thread클래스를 상속한 class의 인스턴스를 생성한 후 이 인스턴스의 start()메소드를 호출한다.
MyThread1 th1 = new MyThread1();
th1.start();
//방법2 : Runnable 인터페이스를 구현한 class의 인스턴스를 생성한 후
//이 인스턴스를 Thread객체의 인수턴스를 생성할때 생성자의 매개변수로 넘겨준다.
//이때 생성된 Thread객체의 인스턴스의 start()메소드를 호출한다
// void run()을 Runnable이라는 인터페이스로 구현한것이다
Runnable r = new MyThread2();
Thread th2 = new Thread(r);
th2.start();
//방법3 : 익명클래스를 이용하는 방법 : 재사용 할 필요 없을때
//Runnable 인터페이스를 구현한 익명클래스를 Tread인스턴스를 생성할때 매개변수로 넘겨준다
Thread th3 = new Thread(new Runnable() {
@Override
public void run() {
for(int i =1; i<=200; i++) {
System.out.print("@");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
// 시간은 밀리세컨드 단위를 사용한다
// 즉 1000=1초
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
th3.run();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for(int i =1; i<=200; i++) {
System.out.print("*");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
// 시간은 밀리세컨드 단위를 사용한다
// 즉 1000=1초
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyThread2 implements Runnable{
public void run() {
for(int i =1; i<=200; i++) {
System.out.print("$");
try {
// Thread.sleep(시간) => 주어진 시간동안 작업을 잠시 멈춘다
// 시간은 밀리세컨드 단위를 사용한다
// 즉 1000=1초
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
프로그램 종료시점: 메인스레드를 포함한 모든 스레드가 모두 종료된 경우!
클래스는 다중상속이 안되기 때문에 방법1을 사용할 경우 이미 상속된 객체가 있다면 스레드로 돌리지 못한다는 단점이 있다. → 이런경우 방법2를 써서 Runnable을 implement하면 됨!
상태 | 설명 |
---|---|
NEW | 스레드가 생성되고 start()가 호출되지 않은 상태 |
RUNNABLE | 실행중 또는 실행 가능한 상태 ( start()호출한 상태 / 다른애가 CPU반납하길 기다리는 상태 ) |
BLOCKED | 일시정지 된 상태 |
WAITING, TIMED_WAITING | 스레드 작업 종료되진 않았지만 실행가능하지 않은 일시정지 상태, TIMED_WAITING은 일시정지시간이 지정된경우 |
TERMINATED | 스레드의 작업이 종료된 상태 |
public class T03_ThreadTest {
public static void main(String[] args) {
// 스레드의 수행시간 체크해보기
Thread th = new Thread(new MyRunner());
/*
UTC(Universal Time Coordinated 협정세계표준시)를 사용하여 1970년 1월1일
0시0분0초를 기준으로 경과한 시간을 밀리세컨드 단위로 나타낸다
*/
long startTime = System.currentTimeMillis();
th.start(); // 스레드 작업 시작
try {
th.join(); // 현재 실행중인 스레드에서 작업중인 스레드(지금은th스레드)가 종료될때까지 기다린다
} catch (InterruptedException e) {
// TODO: handle exception
}
long endTime = System.currentTimeMillis();
System.out.println("경과시간: "+(endTime-startTime));
}
}
// 1~1000000000 까지의 합계를 구하는 스레드 클래스
class MyRunner implements Runnable{
@Override
public void run() {
long sum = 0;
for(int i =0; i<=1000000000; i++) {
sum += i;
}
System.out.println("합계: "+sum);
}
}
public class T04_ThreadTest {
/*
1~20 억까지의 합계를 구하는데 걸린 시간 체크하기
전체 합계를 구하는 작업을 단독으로 했을 때 (1개의 스레드를 사용했을때)와
여러 스레드로 분할해서 작업할때의 시간을 확인해보자.
*/
public static void main(String[] args) {
// 단독으로 처리했을때
SumThread sm = new SumThread(1L, 2000000000L);
long startTime = System.currentTimeMillis();
sm.start();
try {
sm.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("단독으로 처리할떄의 처리시간: "+(endTime-startTime));
System.out.println("\n\n");
//여러 스레드가 협력해서 처리할때
SumThread[] sumThs = new SumThread[] {
new SumThread(1L, 500000000L),
new SumThread(500000001L, 1000000000L),
new SumThread(1000000001L, 1500000000L),
new SumThread(1500000001L, 2000000000L)
};
startTime = System.currentTimeMillis();
for(int i =0; i<sumThs.length;i++) {
sumThs[i].start();
}
for(SumThread th : sumThs) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
endTime = System.currentTimeMillis();
System.out.println("협력해서 처리 했을때의 시간: "+(endTime-startTime));
}
}
class SumThread extends Thread {
private long max,min;
public SumThread(long min, long max) {
this.min = min;
this.max=max;
}
@Override
public void run() {
long sum = 0L;
for(long i=min; i<=max; i++) {
sum+=i;
}
System.out.println(min+" ~ "+max+" 까지의 합 : "+sum);
}
}
import javax.swing.JOptionPane;
public class T05_ThreadTest {
public static void main(String[] args) {
String str = JOptionPane.showInputDialog("아무거나 입력하세요: ");
System.out.println("입력값은 "+str+"입니다.");
for(int i =10; i>=1; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
한 스레드에서 실행하면 동시 실행이 불가능하므로 입력을 끝낸 후 10초를 세게 된다.
import javax.swing.JOptionPane;
public class T06_ThreadTest {
public static boolean inputChk;
public static void main(String[] args) {
Thread th1 = new DataInput();
Thread th2 = new CountDown();
th1.start();
th2.start();
}
}
class DataInput extends Thread {
@Override
public void run() {
String str = JOptionPane.showInputDialog("아무거나 입력하세요");
//입력이 완료되면 true
T06_ThreadTest.inputChk = true;
System.out.println("입력한 값은 "+str+"입니다");
}
}
class CountDown extends Thread {
@Override
public void run() {
for(int i=10; i>=1; i--) {
System.out.println(i);
// 입력이 완료되었는지 여부를 검사하고 입력이 완료되면 run()을 종료시킨다. 즉 현재 스레드 종료
if(T06_ThreadTest.inputChk) {
return; //true면 for문 돌 필요 없으니까 return; 으로 근접한 그 메소드 빠져나감 (break는 for문탈출)
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//10초가 경과되었는데도 입력이 없으면 프로그램 종료
System.out.println("10초가 지났습니다. 프로그램을 종료합니다");
System.exit(0);
}
}
Z
두 스레드가 동시에 동작하므로 10초 카운트다운을 하다 입력이 들어오면 카운트다운을 멈춘다.
public class T08_ThreadPriorityTest {
public static void main(String[] args) {
System.out.println(" 최대 우선순위 : "+ Thread.MAX_PRIORITY);
System.out.println(" 최소 우선순위 : "+ Thread.MIN_PRIORITY);
System.out.println(" 보통 우선순위 : "+ Thread.NORM_PRIORITY);
Thread[] ths = new Thread[] {
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest1(),
new ThreadTest2()
};
// 우선순위는 start()를 호출하기 전에 설정해야한다
for(int i=0; i<ths.length; i++) {
if(i==5) {
ths[i].setPriority(10);
}else {
ths[i].setPriority(1);
}
}
// 우선순위 출력
for(Thread th : ths) {
System.out.println(th.getName()+"의 우선순위 : "+th.getPriority());
}
for(Thread th : ths) {
th.start();
}
}
}
// 대문자를 출력하는 스레드
class ThreadTest1 extends Thread {
@Override
public void run() {
for(char ch='A'; ch<='Z'; ch++) {
System.out.println(ch);
// 아무것도 하지 않는 반복문 (시간때우기용)
for (long i=1; i<=1000000000; i++) {}
}
}
}
// 소문자를 출력하는 스레드
class ThreadTest2 extends Thread {
@Override
public void run() {
for(char ch='a'; ch<='z'; ch++) {
System.out.println(ch);
// 아무것도 하지 않는 반복문 (시간때우기용)
for (long i=1; i<=1000000000; i++) {}
}
}
}
// 스레드를 데몬스레드로 설정하기
public class T09_ThreadDaemonTest {
public static void main(String[] args) {
Thread th = new AutoSaveThread();
// 데몬스레드로 설정하기 (start()를 호출하기 전에 설정해야 한다)
th.setDaemon(true);
th.start();
try {
for(int i = 1; i<=20; i++) {
System.out.println("작업 - "+i);
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("메인스레드 종료...");
}
}
// 자동저장 기능을 제공하는 스레드 (3초에 한번씩 저장)
class AutoSaveThread extends Thread {
@Override
public void run() {
while(true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
save(); //저장기능호츌
}
}
public void save() {
System.out.println("작업 내용을 저장합니다...");
}
}
get State(). 처음엔 NEW상태이고 start()할려고 만들어놓은거임
start()하면 Runnable 됨
실행하고있는거, 실행할순 없지만 나에게 CPU할당될때까지 기다리는상태 보두 Runnable
Sleep : 재우면 Runnable에서 Waiting으로 바뀜(시간 부여하면 TimedWaiting) 깨어나면 다시 Runnable
쓰레드의run()이 끝나면 종료상태 즉 Terminate 상태가 된다
// 스레드의 상태를 확인하기
public class T10_ThreadStateTest {
public static void main(String[] args) {
Thread th = new StatePrintThread(new TargetThread());
th.start();
}
}
// 스레드의 상태를 출력하는 클래스
class StatePrintThread extends Thread {
private Thread targetThread; //모니터링 할 스레드 저장용
public StatePrintThread(Thread targetThread) {
this.targetThread = targetThread;
}
@Override
public void run() {
while(true) {
// Thread의 상태 구하기(getState()이용)
Thread.State state = targetThread.getState();
System.out.println("타겟스레드의 상태값: "+state.name());
// NEW 상태인지 검사
if(state == Thread.State.NEW) {
targetThread.start();
}
// 타겟스레드가 종료상태인지 검사
if(state == Thread.State.TERMINATED) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
// 모니터링 대상 스레드 클래스
class TargetThread extends Thread {
@Override
public void run() {
for(long i=1; i<= 1000000000L; i++) {} //시간 지연용
try {
Thread.sleep(1500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
for(long i = 1; i<=1000000000L; i++) {} //시간지연용
}
}
// 3개(명)의 스레드가 각각 알파벳 대문자를 출력하는데 출력을 끝낸 순서대로 결과를 나타내는 프로그램 작성하기
public class T11_DisplayCharacterTest {
static String strRank = "";
public static void main(String[] args) {
Thread[] ths = new Thread[] {
new DisplayCharacter("홍길동"),
new DisplayCharacter("일지매"),
new DisplayCharacter("변학도")
};
for(Thread th : ths) {
th.start();
}
for(Thread th : ths) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("경기 끝...");
System.out.println("------------------------");
System.out.println();
System.out.println(" 경기 결과 ");
System.out.println("순위 : " + strRank);
}
}
// 영어 대문자를 출력하는 스레드 클래스
class DisplayCharacter extends Thread {
private String name;
public DisplayCharacter(String name) {
this.name = name;
}
@Override
public void run() {
for(char ch='A'; ch<='Z'; ch++) {
System.out.println(name + "의 출력 문자 : " + ch);
try {
// sleep()의 값을 200~500사이의 난수로 한다.
Thread.sleep((int)(Math.random() * 301 + 200));
}catch(InterruptedException ex) {
ex.printStackTrace();
}
}
System.out.println(name + " 출력 끝...");
T11_DisplayCharacterTest.strRank += name + " ";
}
}
public class T12_ThreadYieldTest {
public static void main(String[] args) {
Thread th1 = new YieldThread1();
Thread th2 = new YieldThread2();
th1.start();
th2.start();
}
}
class YieldThread1 extends Thread {
public YieldThread1() {
super("양보 스레드");
}
@Override
public void run() {
for(int i=0; i<10; i++) {
System.out.println(Thread.currentThread().getName()
+ " : " + i);
Thread.yield(); // 양보하기
}
}
}
class YieldThread2 extends Thread {
public YieldThread2() {
super("비양보 스레드");
}
@Override
public void run() {
for(int i=0; i<10; i++) {
System.out.println(Thread.currentThread().getName()
+ " : " + i);
}
}
}
중지하는 메소드 : 정리 안하고 꺼버림
비추천 (deprecated)
그렇다면 그 외 다른방법 ? :
1) 상태값 만들어놓고 상태에 따라 적절히 종료시키는것(True,False로 상태값을 만들기)
2) interrupt()메소드를 이용하여 해당 스레드에 interrupt()를 건다. 적절한 액션을 취할수가 있음 sleep()이나 join() 시행시 IntteruptedException 걸릴 수 있어 try-catch로 감싸서 해당 sleep메소드 실행시점에 catch로 빠진다. 그 흐름을 이용해서 빠지고 싶은 구문을 만들어 빠져나와 종료한다.
intterupt 검사 : instance() 메소드나(인터럽트 걸렸으면 True 리턴, break로 적절하게 빠져나오면 ㅇㅋ) static()메소드를(Thread.interrupted 하면 외부에서 인터러티드 걸렸으면 true 리턴됨 빠져나와서 중지되도록 하기..) 이용 (두개 차이점은 static은 두번째 턴 부터는 다시 false로 초기화돼서 인터럽티드가 한번만 유효)
공통으로 바라보는 객체 (=공유객체) 를 통해 주고받음
ex. 원주율정보 담은 ShareData 객체 같은거,,
특정변수 선언시 붙여주면 해당 변수는 최적화 된 상태에서 사용됨
원본 데이터를 항상 바라보고 접근하도록 처리해주는... 때문에 속도는 느리지만 원래 데이터를 확실하게 읽어와준다.
동기화는 왜 필요한가 ? 여럿과 공유하기 때문에 예기치 않은 결과가 발생 할 수 있기 때문에 임계영역을 동기화처리해줘야한다
1) 메소드 자체에 Syncronized 해준다. 메소드 처리중에 본 스레드 외의 스레드는 해당 공유객체에 접근 불가능하게함 (다른 스레드는 기다리고 있음 즉 Blocked(동기화할때 나오는 상태))
2) 동기화블록을 만든다(중괄호를 감싸서 Syncronized)
// 은행의 입출금을 스레드로 처리하는 예제
//(syncronized를 이용한 동기화 처리)
public class T16_SyncAccountTest {
public static void main(String[] args) {
SyncAccount sAcc = new SyncAccount();
sAcc.deposit(10000);
BankThread bth1 = new BankThread(sAcc);
BankThread bth2 = new BankThread(sAcc);
bth1.start();
bth2.start();
}
}
// 은행의 입출금을 관리하는 클래스 정의
class SyncAccount{
private int balance; //잔액
public synchronized int getBalance() {
return this.balance;
}
//입금 처리를 수행하는 메소드
public void deposit(int money) {
balance += money;
}
//출금을 처리하는 메서드(출금성공:true, 출금실패:false 반환)
// 동기화 영역에서 호출하는 메서드도 동기화 처리를 해주어야 한다
// ㄴ getBalance()도 동기화 해줘야 문제가 없단소리
public boolean withdraw(int money) {
if(balance >= money) { //잔액이 많을경우
for(int i=1; i<=1000000000; i++) {} //시간 지연용
balance -=money;
System.out.println("메서드 안에서 balance = "+getBalance());
return true;
} else {
return false;
}
}
}
// 은행업무를 처리하는 스레드
class BankThread extends Thread {
private SyncAccount sAcc;
public BankThread(SyncAccount sAcc) {
this.sAcc = sAcc;
}
@Override
public void run() {
boolean result = sAcc.withdraw(6000); //6000원 인출
System.out.println("스레드 안에서 result = "+result+", balance = "+sAcc.getBalance());
}
}
public boolean withdraw(int money) {} 에 두개가다 들어와서 순서대로 돌기 때문에 -나옴
두개이상 스레드가 한 객체를 보고 작업하면, 이렇게 문제가 발생 할 수 있다 그리고 이 영역을 임계영역이라 하며 동기화 처리가 필요하다.
**synchronized** public boolean withdraw(int money) {
if(balance >= money) { //잔액이 많을경우
for(int i=1; i<=1000000000; i++) {} //시간 지연용
balance -=money;
System.out.println("메서드 안에서 balance = "+getBalance());
return true;
} else {
return false;
}
}
}
락 기능을 제공하는 클래스
ReentrantLock => Read 및 Write 구분 없이 사용하기 위한 Lock클래스로 동기화 처리를 위해 사용
synchronized를 이용한 동기화 처리보다 부가적인 기능이 제공됨
ex) Fairness 설정 등 => 가장 오래 기다린 스레드가 가장 먼저 락을 획득하게 함
ReetrantReadWriteLock => Read 및 Write 락을 구분하여 사용 가능함
재귀적으로 여러번 Read나 Write 락을 획득할 수 있다.(재진입 가능)
여러 스레드가 동시에 read작업은 가능하지만, write작업은 단지 하나의 스레드만 가능함(exclusive)
=> Write보다 Read 위주의 작업이 많이 발생하는 경우에 사용하면 처리량(Throughput)이 좋아진다.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 은행의 입출금을 스레드로 처리하는 예제
// (Lock객체를 이용한 동기화 처리)
public class T17_LockAccountTest {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock(true); // true는 fairness 설정 킨것임! //락 객체 생성
LockAccount lAcc = new LockAccount(lock);
lAcc.deposit(10000);
BankThread2 bth1 = new BankThread2(lAcc);
BankThread2 bth2 = new BankThread2(lAcc);
bth1.start();
bth2.start();
}
}
// 입출금을 담당하는 클래스
class LockAccount{
private int balance;
//Lock 객체 생성 => 되도록이면 private final로 만든다
private final Lock lock;
public LockAccount(Lock lock) {
this.lock = lock;
}
public int getBalance() {
return balance;
}
// 입금하는 메서드
public void deposit(int money) {
//Lock 객체의 lock()메서드가 동기화의 시작이고, unlock()메서드가 동기화의 끝을 나타낸다.
// lock()메서드로 동기화를 설정한 곳에서는 반드시 unlock()메서드로 해제해 주어야 한다.
lock.lock(); // 동기화 시작 (락을 획득하기 전까지 BLOCKED 됨)
balance += money; // 동기화 처리부분
lock.unlock(); // 동기화 끝
}
// 출금하는 메서드(출금성공 : True, 출금실패 : False)
public boolean withdraw(int money) {
boolean chk = false;
if(lock.tryLock()) { // 락 획득 시도( 성공시 true, 실패시 false)
System.out.println(Thread.currentThread().getName() + " : 락 획득 성공!");
//try~catch 블럭을 사용할 경우에는 unlock() 호출은 finally블럭에서 하도록 한다.
try {
if(balance >= money) {
for(int i=1; i<=1000000000; i++) {} //시간지연용
balance -= money;
System.out.println("메서드 안에서 balance = "+getBalance());
chk = true;
}
} catch (Exception ex) {
ex.printStackTrace();
}finally{
lock.unlock(); //락 해제(동기화 끝)
System.out.println("락 해제 (반납) 완료..");
}
}else { //락 획득 실패시..
System.out.println(Thread.currentThread().getName()+" : 락 획득 실패!");
}
return chk;
}
}
// 은행업무를 처리하는 스레드
class BankThread2 extends Thread {
private LockAccount lAcc;
public BankThread2(LockAccount lAcc) {
this.lAcc = lAcc;
}
@Override
public void run() {
boolean result = lAcc.withdraw(6000);
System.out.println(Thread.currentThread().getName()+" 스레드 안에서 result = "+ result+", balance = "+lAcc.getBalance());
}
}
Vector, HashTable등 예전부터 존재하던 Collection들은 내부에 동기화 처리가 되어있다.
그런데 최근에 새로 구성된 Collection 클래스들은 처리가 되어있지않다. (그래서 빠름)
그래서 동기화가 필요한 프로그램에서 이런 Collection들을 사용하려면 동기화 처리를 한 후에 사용해야한다.
Collections에서 동기화 처리하는거 제공.... ..
오류남
import java.util.ArrayList;
import java.util.List;
// 리스트 동기화 처리 예제
public class T18_SyncCollectionTest {
//동기화 처리를 하지 않은 경우
private static List<Integer> list1= new ArrayList<Integer>();
public static void main(String[] args) {
//익명클래스를 이용한 스레드 구현
Runnable r = new Runnable() {
@Override
public void run() {
for(int i = 1; i<=10000; i++){
list1.add(i);
}
}
};
Thread[] ths = new Thread[] {
new Thread(r), new Thread(r),
new Thread(r), new Thread(r), new Thread(r)
};
for(Thread th : ths) {
th.start();
}
for(Thread th : ths) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("list1의 개수: "+list1.size());
}
}
동기화 블록을 써도 되고..
@Override
public void run() {
for(int i = 1; i<=10000; i++){
synchronized (list1) {
list1.add(i);
}
}
}
이렇게 할수있음.. Map도 동일한 방식으로 가능
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// 리스트 동기화 처리 예제
public class T18_SyncCollectionTest {
//동기화 처리를 하지 않은 경우
// private static List<Integer> list1= new ArrayList<Integer>();
//동기화 처리를 한 경우
private static List<Integer> list2= Collections.synchronizedList(new ArrayList<Integer>());
public static void main(String[] args) {
//익명클래스를 이용한 스레드 구현
Runnable r = new Runnable() {
@Override
public void run() {
for(int i = 1; i<=10000; i++){
list2.add(i);
// list.add(i);
}
}
};
Thread[] ths = new Thread[] {
new Thread(r), new Thread(r),
new Thread(r), new Thread(r), new Thread(r)
};
for(Thread th : ths) {
th.start();
}
for(Thread th : ths) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//System.out.println("list1의 개수: "+list1.size());
System.out.println("list2의 개수: "+list2.size());
}
}