Properties
HashTable의 자식클래스
K,V가 String, String 형으로 고정되어 설정 정보를 관리하는데에 최적화 되어있다.
//prop이라는 Properties 생성
Properties prop = new Properties();
요소 설정 : setProperty
prop.setProperty("class", "hello.world.ThankyouJava");
prop.setProperty("url", "https://kh.com");
prop.setProperty("id", "abcdefg");
요소 가져오기 : getProperty
String _class=prop.getProperty("class");
String url = prop.getProperty("url");
String id = prop.getProperty("id");
설정파일 쓰기
try {
prop.store(new FileWriter("test.properties"),"<<test.properties>>");
}catch(IOException e) {
e.printStackTrace();
}
설정파일 읽어오기
Properties prop2=new Properties();
try {
prop2.load(new FileReader("test.properties"));
System.out.println("읽어오기 완료!");
}catch(IOException e) {
e.printStackTrace();
}
내용 1개씩 열람
System.out.println(prop2.getProperty("url"));
System.out.println(prop2.getProperty("class"));
System.out.println(prop2.getProperty("id"));
전체 내용 열람
Set<String> keySet=prop2.stringPropertyNames();
for(String key : keySet) {
String value = prop2.getProperty(key);
System.out.println(key +" = "+value);
}
Thread 쓰레드
프로세스 내에서 실제 작업을 수행하는 작업 최소 단위.
모든 프로세스는 하나 이상의 스레드를 가지며 각각 독립적인 작업 단위를 가짐.
* 프로세스 : 실행중인 하나의 프로그램. 하나의 프로그램이라도 실행 시 마다 개별 프로세스 생성
싱글스레드 : 메인스레드가 종료하면 프로세스도 종료.
멀티스레드 : 실행중인 스레드가 하나라도 있다면 프로세스가 종료되지 않음.
메인스레드는 main()메소드를 실행하면서 시작하고, return을 만나면서 실행을 종료시킴.
멀티프로세스 vs 멀티 스레드
멀티 프로세스는 독립적으로 프로그램을 여러개 실행하는 것.
멀티 스레드는 한개의 프로그램을 실행하되, 내부적으로 작업을 여러개 처리하는 것.
멀티스레드의 장단점
장점
- 자원 효율적으로 사용 가능, 사용자에 대한 응답성 향상됨, 작업이 분리되어 코드가 간결해짐
단점
- 동기화에 주의해야함, 교착상태가 발생하지 않도록 주의해야함, 프로그래밍시 고려할 사항이 많아짐
싱글 스레드
private void test1() {
taskA();
taskB();
}
public void taskA() {
for(int i=0;i<1000;i++) {
System.out.print("+");
}
}
public void taskB() {
for(int i=0;i<1000;i++) {
System.out.print("|");
}
}
다음과 같은 함수가 존재할 때, 싱글 스레드로 작동하기 때문에 결과는 몇번을 실행해도 아래와 같이 나온다.
멀티스레드
1. Thread 상속
Thread th1 = new MyThreadA();
Thread th2 = new MyThreadB(); //부모타입으로 다룸. 다형성
th1.start();
th2.start();
public class MyThreadA extends Thread {
@Override
public void run() {
new ThreadBasicStudy().taskA();
}
}
class MyThreadB extends Thread {
@Override
public void run() {
new ThreadBasicStudy().taskB();
}
}
MyThreadA,B 를 따로 만들어주고 위에 선언한 taskA와 taskB를 사용해서 스레드를 작동시켜본다.
결과는 작동할때마다 바뀐다. MyThreadA와 MyThreadB가 동시에 작동하고 있기 때문이다.
2. Runnable 인터페이스 구현 (활용도 더 높음)
Runnable run1=new CustomThreadA();
Runnable run2=new CustomThreadB();
Thread th3=new Thread(run1);
Thread th4=new Thread(run2);
th4.setPriority(Thread.MAX_PRIORITY); //우선순위 가장 높음
th3.setPriority(Thread.MIN_PRIORITY); //우선순위 가장 낮음
th3.start();
th4.start();
public class CustomThreadA implements Runnable {
@Override
public void run() {
new ThreadBasicStudy().taskA();
}
}
class CustomThreadB implements Runnable {
@Override
public void run() {
new ThreadBasicStudy().taskB();
}
}
쓰레드 우선순위는 1~10까지 존재. 1이 가장 낮고, 10이 가장 높다.
대체적으로 | 가 먼저 실행되지만, 엄청 유의미한 결과는 아니란 것을 알 수 있다.
내부클래스 선언으로 스레드 만들기
- nonstatic 내부클래스 new 파일클래스명().new 내부클래스명()
- static 내부클래스 new 파일클래스명.내부클래스명();
내부클래스 사용 이유)
1. 외부클래스안에서만 사용하기 위해서
2. 외부클래스의 자원에 자유롭게 접근하기 위해서 - 외부클래스의 private자원도 접근이 가능하다.
내부클래스명을 SleepThread 라고 했을 때, 코드를 다음과 같이 짠다.
static class SleepThread implements Runnable{
private char ch;
private long millis;
public SleepThread(char ch,long millis) {
this.ch=ch;
this.millis=millis;
}
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.print(ch);
try {
Thread.sleep(millis); //단위 밀리초
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()); //현재 쓰레드 이름
}
}
private void test1() {
Runnable run1=new SleepThread('+',300); //0.3초
Runnable run2=new SleepThread('|',500); //0.5초
Thread th1=new Thread(run1);
Thread th2=new Thread(run2);
th1.setName("[+쓰레드]");
th2.setName("[|쓰레드]");
th1.start();
th2.start();
}
실행 코드가 test1()과 같을때, + 스레드는 0.3초, | 스레드는 0.5초 간격으로 진행된다. 진행이 끝나면 [+쓰레드] , [ | 쓰레드]가 출력되는데, + 는 간격이 0.3초이므로 먼저 출력된다.
아까처럼 클래스를 두개 만들지 않아도 되어 편리하다.
Daemon 쓰레드
- 리눅스 / 유닉스 계열의 OS에서 백그라운드에서 작동하는 프로그램
- 데몬 쓰레드는 일반 쓰레드에 종속적으로 작동한다.
- 사용자 글쓰기 중간에 임시저장 용으로 쓰이는 쓰레드.
class를 선언하여 사용하는 것은 똑같고, 다만 선언은 다음과 같이 하면 된다.
* CountDown이 class명
Thread countD = new Thread(new CountDown(10),"[카운트다운]");
countD.setDaemon(true); // ---(1)
countD.start();
(1) 이 setDaemon을 통해 일반스레드가 아닌 데몬스레드를 사용한다고 선언한 부분이다.
join
- 특정쓰레드가 종료되기까지 대기(waiting)되었다 진행하는 메소드
- 특정시간만 대기했다 진행할 수 있음.
join은 try-catch문에서 사용한다.
예제를 보자.
private void test4() {
Thread cd=new Thread(new CountDown(10),"[카운트다운]");
cd.start();
try {
// cd.join(); //현재쓰레드가 cd쓰레드를 기다린다.
cd.join(3000);//3초뒤 시작
} catch (InterruptedException e) {
e.printStackTrace();
}
}
위와 같이 사용하면 된다.
interrupt
- 특정쓰레드를 종료시키는 메소드
- 메인쓰레드에서 카운트다운쓰레드를 종료시킴
- InterruptException 발생시켜서 흐름을 분기처리
private void test5() {
Thread cd=new Thread(new CountDown(10),"[카운트다운]");
cd.start();
JOptionPane.showMessageDialog(null, "OK를 누르면 카운트다운이 멈춥니다.");
cd.interrupt(); //현재쓰레드에서 cd쓰레드에 interrupt!
}
다음과 같이 사용한다.
CountDown 의 try-catch문에서 e.printStackTrac()를 주석처리하면
다음과 같이 나오고,
주석처리하지 않으면
다음과 같이 나오게 된다.
Synchronization
- 멀티쓰레딩 프로그래밍에서 쓰레드간 공유자원에 대한 처리
- 공유자원에 대해 사용순서를 정해준다. lock을 획득한 쓰레드만 사용이 가능하다.
멀티쓰레드 2개를 동시에 사용하는 계산기가 있다. 1000원에서 계산을 시작했을때, 멀티쓰레드는 순서 상관없이 진행되기 때문에 다음과 같이 나온다 .
atm1 , atm2는 별개인데, 처음에 atm2에서 300원을 출금했는데 700원이 아닌 500원이 남고,
atm1도 800원이 남아야 하는데 500원이 남고...
멀티쓰레드가 여기저기 사이에 껴서 진행되어 연산이 엉망진창으로 진행된다.
이때 synchronized를 쓰면 모든게 해결된다.
사진 설명을 입력하세요.
계산이 정상적으로 실행되는 것을 볼 수 있다.
synchronized사용법
1. synchronized 메소드로 작성
메소드를 가진 객체(Account)가 격리공간으로 지정됨
2. 메소드 내부에 synchronized 블럭을 작성
지정한 객체가 격리공간으로 지정됨. 국지적으로 지정이 가능하므로 이 방법을 쓰는 것이 더 좋다. 격리공간이 커질수록 성능은 떨어짐.
1. public void 함수명 (int 변수){} → public synchronized void 함수명 (int 변수){} 로 바꾸기
2. 메소드 내부에 아래와 같이 작성하기
synchronized(this){
//동기화 처리해야 할 객체 내용
}