1) 멀티스레드
1-1) 프로세스와 스레드
1-2)스레드 ,Runnable
1-3) 스레드의동기화
1-4) wait , notify , notifyAll
1-5)데몬스레드
1-6)Join() 메서드
1-7) Thread 우선순위
1-8)스레드 총 정리
1-9) 멀티프로세스
1-1) 프로세스와 스레드
1-2) 스레드,Runnable
코드로 보는 예시 )
Thread생성
1.Thread 클래스 상속 , run()메서드구현
package ex1_create;
/*
* Thread 클래스를 상속
* run() 메서드 구현 : 스레드의기능
*/
class Thread1 extends Thread{
Thread1(String name) {
super(name);//Thread 이름설정(getName()으로호출가능)
}
//Thread 수행 해야할 메서드
@Override
public void run() {//Running상태 (run()메서드 실행중인 상태)
for (int i = 0; i < 5; i++) {
System.out.println(i+"="+getName());
try {
sleep(1000);//(1000ms)1초동안대기(대기상태)
//이함수로인해 멀티쓰레드가 됨.
//대기하는동안 Thread2실행
}
catch (InterruptedException e) {}
}
}
}
public class ThreadEx1 {
public static void main(String[] args) {
System.out.println("main시작");
Thread1 t1 = new Thread1("First");
Thread1 t2 = new Thread1("second");//Thread객체 생성
t1.start();//t1 객체 Runnable상태
t2.start();//t2 객체 Runnable상태
/*
* start() : 스레드의 시작.병렬화시킴 , 멀티쓰레드환경
* 1.스택영역을 병렬화시킨다
* 2.병렬화된 스택영역에 run()메서드 호출
*
*/
System.out.println("main 종료");
}
}
main시작
main 종료
0=second
0=First
1=First
1=second
2=second
2=First
3=second
3=First
4=First
4=second
2.Runnable인터페이스 구현 , run()추상메서드구현
* --> 구현클래스는 Thread 객체가 아님
* --> new Thread(Runnable) 형태로 Thread객체생성
--Runnable 인터페이스는 FunctionalInterface임( 람다 방식 사용 가능)
//추상메서드가 하나만존재하는 인터페이스
package ex1_create;
class Runnable1 implements Runnable{
@Override
public void run() {//Running 상태
//Thread.currentThread() : static메서드 (현재실행중인 쓰레드객체 반환)
for (int i = 0; i < 5; i++) {
System.out.println(i+":"+Thread.currentThread().getName());
try {
Thread.sleep(1000);//대기상태
}
catch(InterruptedException e) {}
}
}//run메서드종료 : Dead상태
}
public class RunnableEx1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"쓰레드 시작");
Runnable r = new Runnable1();//r : Runnable객체
Thread t1 = new Thread(r,"First");//Thread객체 생성(r,name)
Thread t2 = new Thread(r,"Second");//name은 적지않아도 실행에는 문제가없다
//(Thread-0과 같은이름이 주어짐)
t1.start();t2.start();//병렬환경의 run()호출.Runnable상태
System.out.println(Thread.currentThread().getName()+"쓰레드 종료");
}
}
/*
main쓰레드 시작
main쓰레드 종료
0:Second
0:First
1:Second
1:First
2:First
2:Second
3:Second
3:First
4:First
4:Second
*/
람다방식을 이용해서 Runnable을 구현하지않고 위에와 같은결과가 나오게 해보자
package ex1_create;
public class Exam1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"시작");
Runnable r=()->{
for (int i = 0; i < 5; i++) {//Running상태
System.out.println(i+":"+Thread.currentThread().getName());
try {
Thread.sleep(1000);//대기상태
}
catch(InterruptedException e) {}
}//dead상태
};
Thread t1 = new Thread(r,"하하");
Thread t2 = new Thread(r,"재석");
t1.start();t2.start();//Runaable상태
System.out.println(Thread.currentThread().getName()+"종료");
}
}
/*
main시작
main종료
0:재석
0:하하
1:하하
1:재석
2:재석
2:하하
3:재석
3:하하
4:재석
4:하하
*/
1 - 3)쓰레드의 동기화
동기화란
멀티스레드는 병렬로 여러 스레드가 같은 메모리 공유 병렬환경에서 스레드의 실행 제어필요
synchronized 예약어 사용
동기화가 되지않은 스레드의 예
package ex2_synchronized;
class PrintThread extends Thread{
char ch;
PrintThread(char ch){
this.ch = ch;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 80; j++) {
System.out.print(ch);
}
System.out.println();
}
}
}
public class PrintThreadEx1 {
public static void main(String[] args) {
PrintThread t1 = new PrintThread('A');
PrintThread t2 = new PrintThread('B');
PrintThread t3 = new PrintThread('C');
t1.start();t2.start();t3.start();
}
}
/*
AAAAAAAAAABBBCCCCCCCCCCCCCCCCCAAAAAAAAAAAABBBBBBBBBBBBBCCAAAAAABBBBBBBBBCCCCCCCAAAAAAAABBBBBBBBBBCCCCCCCCAAAAAAAABBBBBBBBBCCCCCCCCCAAAAAAAAAAAAABBBBBCCCCCCCCCAAAAAAAAAABBBBBBBBBBBCCCCCCCCCCCAAAAAAAAABBBBBBBBCCCCCCCCAAAABBBBBBBBCCCCCC
AAAAAAABBBB
BBCCC
CCCCCAAAAAAAAAABBBBBBBBBBCCCCCCCCAAAAAAAABBBBBBBBCCCCCCCCCAAAAAAAABBBBBBBBCCCCCCCCAAAAABBBBBBBBBCCCCCCAAAAAAAABBBCCCCCCCCAAAAAAAAAABBBBBBBBCCCCCCCCAAAAAAAABBBBBBBBCCCCCCCCAAAAAAAABBBBBBBBCCCCCCCCAAAAAAAABBBBBBBBCCCCCCCC
AAAAABBBBBBBCCCC
CCCAAAAAAAAB................
*/
동기화를 시킨 스레드의 예(동기화블럭 사용)
동기화블럭 예
* 1.synchronized 예약어 사용
* 2.lock 객체는 유일해야한다.(static)
* 3.lock 기본자료형은 안됨(int double 등 불가능)
synchronized(Lock) { ... }:
동기화블럭(...끝날때까지 다른객체가 접근 불가능)
package ex2_synchronized;
class PrintThread2 extends Thread{
char ch;
static Object Lock = new Object();
//static이 아니라 인스턴스멤버라면 각각 다른 Lock객체가 생기는것임
PrintThread2(char ch){
this.ch = ch;
}
@Override
public void run() {
synchronized(Lock) {
//Lock이 Object타입이므로 Thread객체가 들어갈 수 있음.
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
System.out.print(ch);
}
System.out.println();
}
}
}
}
public class PrintThreadEx2 {
public static void main(String[] args) {
PrintThread2 t1 = new PrintThread2('A');
PrintThread2 t2 = new PrintThread2('B');
PrintThread2 t3 = new PrintThread2('C');
t1.start();t2.start();t3.start();
}
}
/*
AAAA
AAAA
AAAA
AAAA
CCCC
CCCC
CCCC
CCCC
BBBB
BBBB
BBBB
BBBB
*/
하나의 객체가 끝날떄까지 다른객체가 끼어들지 못하는것을 확인!!!
동기화메서드 사용
synchronized
package ex2_synchronized;
/*
* 동기화메서드방식
*
* 동기화영역 , 임계영역.synchronized예약어사용
* 1. 동기화 블럭방식
* static Object Lock=new Object();
* synchronized(Lock 객체){..}
*
* 2. 동기화 메서드방식
* 메서드에 synchronized 예약어사용
* (메서드를 lock으로사용함)
*
*
*
*
*/
class PrintThread3 extends Thread{
Printer prt;
char ch;
PrintThread3(Printer prt , char ch){
this.prt = prt;
this.ch = ch;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
prt.printChar(ch);
}
}
}
//------------------------------------------------------------
class Printer{
public synchronized void printChar(char ch) {//동기화메서드
for (int i = 0; i < 5; i++) {
System.out.print(ch);
}System.out.println();
}
}
public class PrintThreadEx3 {
public static void main(String[] args) {
Printer p = new Printer();
PrintThread3 t1 = new PrintThread3(p, 'A');
PrintThread3 t2 = new PrintThread3(p, 'B');
PrintThread3 t3 = new PrintThread3(p, 'C');
t1.start();t2.start();t3.start();
}
}
/*
AAAAA
AAAAA
AAAAA
AAAAA
AAAAA
CCCCC
CCCCC
CCCCC
CCCCC
CCCCC
BBBBB
BBBBB
BBBBB
BBBBB
BBBBB
*/
동기화 정리
동기화 영역 ( synchronized 예약어 사용) | |
동기화블록 | static Object Lock=new Object(); * synchronized(Lock 객체){..} * lock객체는 공유객체여야한다 * 모든스레드가 lock객체를공유(static) |
동기화메서드 (Thread safe 메서드) |
메서드에 synchronized 예약어사용 * (메서드를 lock으로사용함) * 동기화메서드는유일해야한다. * 공유객체의 메서드여야한다. |
1-4) wait() , notify() , notifyAll() 메서드
1. 동기화영역에서만 사용 가능
2.Object클래스의 멤버다 --> 모두 사용가능
3.wait()
현재 실행 중이 스레드를 대기상태로 변환. Lock 해제됨
notify , notifyAll 메서드로만 해제 가능함
4.notify()
wait() 상태인 스레드 중 한 개를 실행 가능 상태로 변환함. 개발자가 실행 가능 상태로
변경되는 스레드 (지정 불가)
5.notifyAll()
wait() 상태인 모든 스레드를 실행 가능 상태(Runnable)로 변환함
예제)
package ex2_synchronized;
/*
* mother , son 스레드는 하나계좌를 공유
* 계좌에잔액이없다면 son은 wait()
* 잔액이있다면 mother wait()
* mother : 1 ,2,3만원 중 한개를 입금 후 son을 깨운다
* son : 잔액을 0으로만들고 mother을 깨움
*/
class Account{//공유객체
int money;
synchronized void output() {//son Thread
while(money==0) {
try {
wait();//동기화영역에서만 사용가능함
}catch (InterruptedException e) {}
}
notify();//동기화영역에서만 사용가능함
//wait()상태의 스레드중 한개만 Runnable상태로 변경함(지정불가)
System.out.println(Thread.currentThread().getName()+":"+
money+"원 출금");
money = 0;
}
synchronized void input(){//mohter Tread
while(money>0) {
try {
wait();
}catch (InterruptedException e) {}
}
//money가 0이면 실행
money = ((int)(Math.random()*3)+1)*10000;//1~3 *10000
notifyAll();//모든스레드를 깨움(Son을 깨움)
System.out.println(Thread.currentThread().getName()+":"+
money+"원 입금");}
}
//--------------------------------------------------------------------
class Mother extends Thread{
Account account;
Mother(Account account){
super("Mother");
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
account.input(); //입금(1만원2만원3만원 중 랜덤)
try {
sleep((int)(Math.random()*3000));
}catch (InterruptedException e) {}
}
}
}
//--------------------------------------------------------------------
class Son extends Thread{
Account account;
Son(Account account){
super("Son");
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
account.output();//초기에는 money가 없기에 wait()상태에들어가게됨
try {
sleep((int)(Math.random()*1000));//0~1000ms 사이의랜덤한 sleep
}catch (InterruptedException e) {}
}
}
}
//--------------------------------------------------------------------
public class AccountEx1 {
public static void main(String[] args) {
Account account = new Account();
Thread m1 = new Mother(account);
Thread m2 = new Son(account);
m1.start();m2.start();
}
}
Mother:10000원 입금
Son:10000원 출금
Mother:20000원 입금
Son:20000원 출금
Mother:10000원 입금
Son:10000원 출금
Mother:10000원 입금
Son:10000원 출금
Mother:10000원 입금
Son:10000원 출금
Mother:30000원 입금
Son:30000원 출금
Mother:10000원 입금
Son:10000원 출금
.........
1-5)데몬스레드
데몬 스레드
* 1.일반스레드의 보조기능담당
* 2.일반스레드가 종료되면 데몬스레드도 종료
* 3.가비지콜렉터가 대표적인 데몬스레드
* 4.무한반복형태로구현
package ex4_daemon;
class DaemonThread extends Thread{
/*
* public DaemonThread(String name) { super(name); }
*/
@Override
public void run() {
while(true) {
/*
* Thread[#21,Thread-1,5,main]
* Thread[번호,스레드이름,우선순위,스레드그룹]
*/
System.out.println(this);
try {
sleep(100);
}catch (InterruptedException e) {}
}
//run()의 while문은 break문없이
//무한으로 this를 출력한다
// 메인스레드가종료되면 끝날것임
}
}
public class DaemonEx1 {
public static void main(String[] args) throws InterruptedException{
System.out.println(Thread.currentThread().getName()+"스레드 시작");
DaemonThread t1 = new DaemonThread();
DaemonThread t2 = new DaemonThread();
t1.setDaemon(true);t2.setDaemon(true);//데몬스레드로지정
t1.start();t2.start();
Thread.sleep(2000);//mainThread를 2초간 sleep 후 종료(종료 시 daemon스레드종료)
System.out.println(Thread.currentThread().getName()+"스레드 종료");
}
}
main스레드 시작
Thread[#21,Thread-0,5,main]
Thread[#22,Thread-1,5,main]
Thread[#21,Thread-0,5,main]
Thread[#22,Thread-1,5,main]
Thread[#21,Thread-0,5,main]
Thread[#22,Thread-1,5,main]
main스레드 종료
Thread[#21,Thread-1,5,main]
* Thread[번호,스레드이름,우선순위,스레드그룹]
데몬스레드는 일반스레드가 종료되면 강제종료됨
그러므로
while문의 break가없어도 강제종료가 가능해짐
Daemon Thread 활용 예제
해당코드는 3초마다 내가설정한 문자열에서 랜덤하게 List에 들어가게된다
(데몬스레드 사용하므로 메인코드가끝나게되면
데몬스레드의 while문도 강제종료)
데몬스레드가 아니라면 영원히 끝나지않는 코드가 될것임....
나는 입력을통해 List에 요소를 삭제한다.
List의 크기가 0이된다면 break;
(난이도를 올리려면 interval의 숫자를줄여보자)
package ex4_daemon;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/*
* 타자연습프로그램
*/
class DataAddThread extends Thread{
List<String> words;
String[] data;
int interval;
public DataAddThread(List<String> words, String[] data, int interval) {
this.words = words;
this.data = data;
this.interval = interval;
}
@Override
public void run() {
while(true) {
words.add(data[(int)(Math.random()*data.length)]);//중복되서 추가될수도있음(리스트는중복허용)
try {
sleep(interval);//interval만큼 sleep
}
catch(InterruptedException e) {}
}
}
}
//-------------------------------------------------------------------------
public class DaemonThreadEx2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List<String> words = new ArrayList<>();//삭제대상이되는 문자들을 모아놈
String[] data= {" 태연 "," 유리","윤아"," 효연 ","수영","서현",
"티파니","써니","유재석","강호동"};
int interval = 3*1000;//sleep시간
words.add(data[0].trim());//일단 0 번인덱스를 list에 추가(공백을 제거하고넣어야함)
DataAddThread t1 = new DataAddThread(words,data,interval);
t1.setDaemon(true);//데몬스레드설정
t1.start();//Runnable
while(true) {
System.out.println(words);
System.out.printf("-->");
String input = scan.next().trim();//공백을제거한 입력문자
words.remove(input);//입력된이름을 리스트의요소에서제거
if(words.size()==0) {
break;
}
}
System.out.println("프로그램종료");
}
}
[태연]
-->태여ㅏ
[태연, 수영]
-->태연
[수영, 윤아]
-->수영
[윤아]
-->윤아
프로그램종료
1-6)Join() 메서드
모든스레드가 종료할때까지 main메서드가 대기함
package ex5_other;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/*
* Join메서드 : 스레드가 종료할때까지 join() 메서드를 호출한 메서드가 대기함
*
* 1~1000까지의 합 구하기
* t1~t5 까지 쓰레드가 200씩나누어 합을 구하고 main은 대기하고있다가 전체합을 출력
*/
class SumRunnable implements Runnable{
int firstNum,lastNum;
int sum;
public SumRunnable(int firstNum, int lastNum) {
this.firstNum = firstNum;
this.lastNum = lastNum;
}
@Override
public void run() {
for(int i=firstNum;i<=lastNum;i++) {
sum+=i;
}
}
}
public class JoinEx1 {
public static void main(String[] args) throws InterruptedException {
//List = Arrays.asList(값들)
List<SumRunnable> list = Arrays.asList(new SumRunnable(1, 200),new SumRunnable(201, 400),
new SumRunnable(401, 600),new SumRunnable(601, 800),new SumRunnable(801, 1000));
ArrayList<Thread> list1 = new ArrayList<>();
for (Runnable s : list) {//list에있는 Runnable의 수만큼 쓰레드생성
list1.add(new Thread(s));
}
for (Thread t : list1) {//모든쓰레드 start
t.start();
}
for (Thread t1 : list1) {//모든쓰레드 join()
t1.join();
}
//모든스레드의 상태가 dead일 경우 실행
int sum=0;
for (SumRunnable s : list) {//쓰레드의 모든 합
sum+=s.sum;
}
System.out.println("전체합계 : "+sum);
}
}
전체합계 : 500500
1-7)Thread 우선순위
* Runnable상태 --> Running 상태 변경시 스케쥴러가 관여함.개발자의 영역X
* 우선순위를 설정해서 실행상태의 확률을 높일수있음.
* 우선순위 1~10 까지
* 10 : 가장높은 우선순위
* 5 : 기본우선순위
* 1 : 가장낮은우선순위
setPrioriy 로 우선순위를 설정함
package ex5_other;
class ThreadPriority extends Thread{
ThreadPriority(String name) {
this(name,Thread.NORM_PRIORITY);
}
ThreadPriority(String name,int p) {
super(name);
this.setPriority(p);
}
@Override
public void run() {
try {
sleep(200);
}
catch (InterruptedException e) {}
for (int i = 0; i < 10; i++) {
System.out.println(this.toString());
}
}
}
public class PriorityEx1 {
public static void main(String[] args) {
System.out.println("높은 우선순위 : "+Thread.MAX_PRIORITY);
System.out.println("기본 우선순위 : "+Thread.NORM_PRIORITY);
System.out.println("낮은 우선순위 : "+Thread.MIN_PRIORITY);
ThreadPriority t1 = new ThreadPriority("first", 1);
ThreadPriority t2 = new ThreadPriority("second");
ThreadPriority t3 = new ThreadPriority("Third",10);
t1.start();t2.start();t3.start();
System.out.println("@@"+Thread.currentThread().getName()+"종료@@");
//우선순위가 높다고 해당쓰레드를 모두끝낼때까지 아무것도실행되지않는게 아님!
//우선순위가높은것을 먼저 처리하려고한다.(확률적으로 우선순위가낮은게 밑에있다)
}
}
/*
높은 우선순위 : 10
기본 우선순위 : 5
낮은 우선순위 : 1
@@main종료@@
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
Thread[#22,second,5,main]
Thread[#23,Third,10,main]
Thread[#21,first,1,main]
Thread[#22,second,5,main]
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#23,Third,10,main]
Thread[#21,first,1,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#22,second,5,main]
Thread[#23,Third,10,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
Thread[#21,first,1,main]
*/
1-8) 쓰레드 총 정리
1.스레드 생성
* 1) Thread 클래스상속 , run메서드 오버라이딩
* 2) Runnable 인터페이스구현 , run메서드오버라이딩 , Thread객체에 주입
2.스레드상태
* Thread 생성상태 : new Thread();
* Runnable 상태 : start()메서드 실행 후
* Running 상태 : start()에 의해 run()메서드가 실행중인 상태
*
* 대기상태 : sleep(), 동기화 lock획득실패,
* wait() : notify() or notifyAll로 꺠우지않으면 무한대기
* 대기상태해제시 Runnable상태로 변경
* Dead 상태 : run메서드를 모두 실행하고 종료된 상태
3.주요메서드
* (1) start() : 병렬화 기능 , run메서드 호출 , new 상태-->Runnable상태
* (2) run() : 쓰레드의기능 , 스레드가 실행하는 메서드.
* (3) sleep(ms) : 밀리초만큼 대기
* (4) setDaemon(boolean) : true인경우 DaemonThread로 사용하곘단소리
* (start 하기전에 실행)
Daemon스레드 :일반 스레드종료시 함께종료
* (5) join() : 해당스레드가 종료시까지 호출한메서드가 대기
* (6) setPriority(1~10) : 우선순위설정
* (7) interrupt() : InterruptedException발생시킴
4.동기화 : Synchronized 예약어사용
* 동기화블럭 : lock설정 , 공유객체로 lock설정해야함
* 동기화메서드 :공유객체의 메서드여야함(유일해야함)
* 메서드자체가 동기화영역
5.wait , notify , notifyAll
* Object 멤버
* 동기화영역에서만 사용 가능
1-9) 멀티프로세스
* 멀티프로세스예제
* Process : Os상에서 실행되는프로그램
* Runtime.getRuntime() : OS로부터 프로세스를 실행권한 얻기
package ex5_other;
import java.io.IOException;
public class ProcessEx1 {
public static void main(String[] args) throws IOException, InterruptedException {
Process p1 = Runtime.getRuntime().exec("mspaint.exe");//그림판
Process p2 = Runtime.getRuntime().exec("notepad.exe");//메모장
p1.waitFor();
System.out.println("main종료");
}
}
그림판과 메모장을 열어 라는뜻
'부트캠프(Java)' 카테고리의 다른 글
자바/스프링 부트캠프 21일차 (1) | 2025.02.28 |
---|---|
자바/스프링 부트캠프 19일차(람다식과 스트림 ) (2) | 2025.02.26 |
자바/스프링 부트캠프 17일차( 보조스트림 ,객체직렬화 , 파일) (1) | 2025.02.25 |
자바/스프링 부트캠프 16일차 ( 반복자 , 스트림) (0) | 2025.02.21 |
자바/스프링 부트캠프 15일차( Collection {set , map} ) (0) | 2025.02.20 |