Connection은 데이터베이스 서버와의 연결을 나타내며, 데이터베이스와의 통신을 위해 JDBC를 사용하여 설정 및 제어하는 데 사용되는 객체
어떤 SQL 문장을 실행시키기 전에 우선 Connection 객체가 있어야 됨
Connection 객체를 반환하는 이유는 데이터베이스와의 연결을 나타내는 객체를 사용할 수 있도록 하기 위함
완성이 되면 밑에 코드로 DAO에서 만드는 메소드에서 DB 접속 가능
Connection conn = this.getConnection();
ArrayList로 DB 내용 확인 하기
public ArrayList<ScoreDto> selectList() {
try {
Connection conn = this.getConnection();
ArrayList<ScoreDto> list = new ArrayList<>(); 변수는 조회된 데이터를 담기 위한 리스트
Statement stmt = conn.createStatement(); 이 객체는 SQL 실행하기 위해 사용
ResultSet rs = stmt.executeQuery("select num, name, kor, eng, math from score");SELECT문의 결과를 저장하는 객체
while (rs.next()) { ResultSet 객체의 다음 행으로 이동합니다. 행이 존재하는 동안 반복문을 실행
int num = rs.getInt("num");메서드를 호출하여 현재 행의 각 열의 값을 가져옵
String name = rs.getString("name");
int kor = rs.getInt("kor");
int eng = rs.getInt("eng");
int math = rs.getInt("math");
ScoreDto dto = new ScoreDto(num, name, kor, eng, math);가져온 값을 사용하여 ScoreDto 객체 dto를 생성
list.add(dto);생성된 dto를 list에 추가
}
return list; 반환
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;예외 발생하면 해당 예외를 처리하고 null을 반환
}
테이블의 데이터를 조회하여 ScoreDto 객체의 리스트를 반환하는 기능을 수행
총점 ( 리턴 타입, num 참조 )
public int total(int num) { num 값과 일치하는 행만을 선택하기 위해 사용
int total = 0; 점수의 총합을 저장하기 위한 변수
try {
Connection conn = this.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT SUM(kor + eng + math) AS total_score FROM score WHERE num = " + num);
if (rs.next()) { rs.next() 메서드를 호출하여 ResultSet 객체의 다음 행으로 이동
행이 존재하는 경우, 즉 총합 결과가 존재하는 경우에만 아래의 블록을 실행
total = rs.getInt("total_score"); "total_score"라는 별칭을 가진 열의 값을 가져와 total 변수에 저장
}
} catch (Exception e) {
// 예외 처리
e.printStackTrace();
}
return total; 해당 값을 반환
}
ScoreServiceImp 클래스에 밑에 코드처럼 메소드를 재정의 해줌 매개변수, return 타입 확인
public int total(int num) {
return dao.total(num);
}
마지막으로 JSP에서 호출할 때는 <%=service.total(dto.getNum()) %> 이렇게 호출하면 됨
package sec01.exam01;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetEx {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
// set.add(123); 문자열만 가능
set.add("123");
set.add("김길동");
set.add("가나다");
Iterator<String> it = set.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
// for(String str : set) {
// System.out.println(str);
// }
}
}
hashCode()와 equals() 메소드 재정의
정의
package sec01.exam02;
import java.util.Objects;
public class Member {
public String name;
public int age;
public Member(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
System.out.println("equals");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Member other = (Member) obj;
return age == other.age && Objects.equals(name, other.name);
}
@Override
public int hashCode() {
System.out.println("hashCode");
return Objects.hash(age, name);
}
}
실행
package sec01.exam02;
import java.util.HashSet;
import java.util.Set;
public class HashSetEX {
public static void main(String[] args) {
// 데이터 입력 받을 때 중복 검사할 수 있음
Set<Member> set = new HashSet<Member>();
set.add(new Member("홍길동", 30));
set.add(new Member("홍길동", 30));// 인스턴스는 다르지만 내부데이터가 동일하므로 객체 1개만 저장
set.add(new Member("홍길동", 30));
// 두개 비교 후 나머지 한개 비교
System.out.println("총 객체수 : " + set.size()); // 저장된 객체 수 얻기
}
}
<결과>
hashCode
hashCode
equals
hashCode
equals
총 객체수 : 1
데이터 입력 받을 때 중복 검사 가능
중복 저장 방지
Map 컬렉션
Map 컬렉션은 키(key)와 값(Value)으로 구성된 Map.Entry 객체를 저장하는 구조를 가지고 있다.
Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다. 키와 값은 모두 객체
키는 중복 저장될 수 없지만 값은 중복 저장이 가능, 만약 기존에 저장된 키와 동일한 키로 값을 저장하면
기존의 값은 없어지고 새로운 값으로 대체 된다.
Map 컬렉션에서 공통적으로 사용 가능한 Map 인터페이스의 메소드들
키로 객체들을 관리하기 때문에 키를 매개값으로 갖는 메소드가 많다.
<K,V> 타입 파라미터는 Map컬렉션을 생성할 때 키와 객체 타입을 결정하라는 뜻
package sec01.exam03;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapEntryEx {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
// 해시맵이라고 부름
map.put("김", 100);
map.put("이", 95);
map.put("박", 90);
// System.out.println(map.get("이"));
// map 구조를 set으로 변경 ( for문 사용할라고 ) 많이 사용됨
// Set<Map.Entry<String, Integer>> es = map.entrySet();
// for(Map.Entry<String, Integer> a1 : es) {
// System.out.println(a1.getKey()); // 키 값 출력
// System.out.println(a1.getValue());// value 값 출력
// }
Set<Map.Entry<String, Integer>> es = map.entrySet();
Iterator<Map.Entry<String, Integer>> it = es.iterator();
while(it.hasNext()) {
Map.Entry<String, Integer> a1 = it.next();
System.out.println(a1.getKey());
System.out.println(a1.getValue());
}
}
}
<결과>
김
100
이
95
박
90
주석 달린 것 처럼 map 구조를 set으로 변경 후 for문으로 사용할 때 많이 사용됨
HashMap
Map 인터페이스를 구현한 대표적인 Map 컬렉션이다. HashMap의 키로 사용할 객체는 hashCode()와 equals()
메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다. 객체가 달라도 동등 객체라면 같은 키로 간주하고 중복
저장되지 않도록 하기 위함이다. 동등 객체의 조건은 hashCode()의 리턴값이 같아야 하고, equals()메소드가
true를 리턴해야 한다.
주로 키 타입은 String을 많이 사용하는데, String은 문자열이 같을 경우 동등 객체가 될 수 있도록 hashCode()와
equals() 메소드가 재정의되어 있다. HashMap을 생성하기 위해서는 키 타입과 값 타입을 타입 파라미터로 주고기본 생성자를 호출하면 된다.
이름을 키로 점수를 값으로 저장하기 예제 ( HashMap 사용 방법 )
package sec01.exam04;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class HashMapEx {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("신용권", 85);
map.put("홍길동", 90);
map.put("동장군", 80);
map.put("홍길동", 95); // 홍길동은 키 값이 같기 때문에 마지막에 저장한 값으로 대체
System.out.println("총 Entry 수 : " + map.size()); // 저장된 총 Entry
// 객체 찾기
System.out.println("\t홍길동 : " + map.get("홍길동"));// 이름(키)으로 점수(값)를 검색
System.out.println();
// 객체를 하나씩 처리
Set<String> keSet = map.keySet();// key set 얻기
//향상된 for문 밑에 Iterator 안쓰고 사용 가능
for(String str : keSet) {
System.out.println(str);
Integer val = map.get(str);
System.out.println(val);
}
// Iterator<String> keIterator = keSet.iterator();
// while(keIterator.hasNext()) {
// String key = keIterator.next();
// Integer value = map.get(key);
// System.out.println("\t" + key + ":" + value);
// }
System.out.println();
// 객체 삭제
map.remove("홍길동");// 키로 Map.Entry를 제거
System.out.println("총 Entry 수 : " + map.size());
// 객체를 하나씩 처리
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();// Map.Entry Set 얻기
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("\t" + key + ":" + value);
}
System.out.println();
// 객체 전체 삭제
map.clear();
System.out.println("총 Entry 수 : " + map.size());
}
}
<결과>
총 Entry 수 : 3
홍길동 : 95
홍길동
95
신용권
85
동장군
80
총 Entry 수 : 2
신용권:85
동장군:80
총 Entry 수 : 0
Hashtable
HashMap과 동일한 내부 구조를 가지고 있다. Hashtable도 키로 사용할 객체는 hashCode()와 equals()
메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다.
차이점은 동기화된 메소드로 구성되어 있기 때문에 멀티스레드가 동싱에 Hashtable의 메소드들을 실행할 수
없고 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다는 점 때문에 멀티 스레드 환경에서
스레드 객체를 생성하고 start() 메소드를 호출하면 바로 실행되는 것이 아니라 실행 대기 상태가 됩니다.
실행 대기 상태란 언제든지 실행할 준비가 되어 있는 상태를 말한다.
실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 다시 실행 대기 상태로 돌아갈 수 있으며, 실행 대기
상태에 있는 다른 스레드가 선택되어 실행 상태가 되기도 한다.
실행 상태에서 run() 메소드의 내용이 모두 실행되면 스레드의 실행이 멈추고 종료 상태가 된다.
스레드 상태
스레드 객체를 생성하고 start() 메소드를 호출하면 곧바로 스레드가 실행되는 것처럼 보이지만 사실은 실행 대기
상태가 된다. 실행 상태의 스레드는 run() 메소드를 모두 실행하기 전에 다시 실행 대기 상태로 돌아갈 수 있다.
그리고 실행 대기 상태에 있는 다른 스레드가 선택되어 실행 상태가 된다.
스레드는 실행 대기 상태와 실행 상태로 번갈아 변하면서, 경우에 따라서 실행 상태에서 일시정지 상태로 가기도
한다. 일시 정지 상태는 스레드가 실행할 수 없는 상태, 일시 정지 상태에서는 바로 실행 상태로 돌아갈 수 없고,
일시정지 상태에서 빠져나와 실행 대기 상태로 가야 한다.
스레드 상태 제어
스레드 상태 제어는 주어진 시간동안 일시 정지시키는 sleep() 메소드와 스레드를 안전하게 종료시키는 stop 플래그,
interrupt()메소드를 사용한다.
interrupt() : 일시 정지 상태의 스레드에서 InterruptedException을 발생시켜, 예외 처리 코드(catch)에서
실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.
sleep(1000) : 1000은 1초 / 주어진 시간 동안 스레드를 일시 정지 상태로 만든다. 주어진 시간이 지나면 자동적으로
실행 대기 상태가 된다.
stop() : 스레드를 즉시 종료한다. 불안전한 종료를 유발하므로 사용하지 않는 것이 좋다.
주어진 시간 동안 일시 정지
try {
Thread.sleep(1000);// 1초 동안 출력
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
해당 코드 처럼 일시 정지 상태에서 주어진 시간이 되기 전에 interrpt()메소드가 호출되면 InterruptedException이발생하기 때문에 예외 처리가 필요하다.
스레드의 안전한 종료
stop 플래그를 이용하는 방법
package sec02.exam01;
public class FlagEx {
public static void main(String[] args) {
// boolean stop; // stop 플래그 필드
boolean stop = false;
int i = 0;
while(!stop) {// true
System.out.println("진행 중");
i++;
if(i > 10) {
stop = !stop;//false되면 멈추기
}
}
System.out.println("완료");
}
}
무한 반복해서 출력하는 스레드
package sec02.exam02;
public class PrintThread1 extends Thread {
private boolean stop;
public void setStop(boolean stop) {
this.stop = stop;
}
public void run() {
while(!stop) {
System.out.println("실행중");
}
System.out.println("자원 정리");
System.out.println("실행 종료");
}
}
1초 후 출력 스레드를 중지
package sec02.exam02;
public class StopFlagEx {
public static void main(String[] args) {
PrintThread1 printThread1 = new PrintThread1();
printThread1.start();
try {
Thread.sleep(1000);// 1초 동안 출력
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
printThread1.setStop(true);
}
}
<결과>
1초동안 실행중 계속 출력 후
자원 정리
실행 종료
interrupt() 메소드를 이용하는 방법
방법 (1) 선언
package sec02.exam03;
public class PrintThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
System.out.println("실행 중");
Thread.sleep(1);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
System.out.println("자원 정리");
System.out.println("실행 종료");
}
}
방법 (2) 선언
package sec02.exam03;
public class PrintThread2 extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
System.out.println("실행 중2");
if(Thread.interrupted()) {
break;
}
}
System.out.println("자원 정리2");
System.out.println("실행 종료2");
}
}
실행
package sec02.exam03;
public class InterruptEx {
public static void main(String[] args) {
// 방법 (1) 실행
// Thread thread = new PrintThread();
// thread.start();
// 방법 (2) 실행
Thread thread2 = new PrintThread2();
thread2.start();
// 방법 (1) 실행
// try {
// Thread.sleep(1000); // 1초 후 출력 스레드를 중지
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
//
// }
// thread2.interrupt();
}
}
데몬 스레드
주 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드이다. 주 스레드가 종료되면 데몬 스레드도 강제 종료
컬렉션 프레임워크
자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할수 있도록 인터페이스와 구현 클래스를 java.util
패키지에서 제공한다. 이들을 총칭해서 컬렉션 프레임워크라고 부른다.
컬렉션은 객체의 저장을 뜻하고, 프레임워크란 사용 방법을 정해놓은 라이브러리를 말한다. 실제로 컬렉션 프레임
워크는 사용 방법을 인터페이스와 실제 객체를 저장하는 다양한 컬렉션 클래스(구현클래스)를 제공한다.
컬렉션 프레임 워크의 주요 인터페이스로는 List, Set, Map이 있습니다.
List 컬렉션
배열과 비슷하게 객체를 인덱스로 관리한다. 차이점은 저장 용량이 자동으로 증가하며, 객체를 저장할 때 자동으로
인덱스가 부여된다는 것이다. 그리고 추가, 삭제, 검색을 위한 다양한 메소드들이 제공된다.
List 컬렉션은 객체 자체를 저장하는 것이 아니라 객체 번지를 참조한다. 그렇기 때문에 동일한 객체를 중복 저장할수 있는데, 이 경우 동일한 번지가 참조 된다. 밑에 이미지 참고
List 컬렉션에는 ArrayList, Vector, LinkedList 등이 있는데, 밑에 이미지는 List 컬렉션에서 공통적으로
사용 가능한 List 인터페이스 메소드이다. 인덱스로 객체를 관리하기 때문에 인덱스를 매개값으로 갖는
메소드가 많다.
메소드의 매개 변수 타입과 리턴 타입에 E라는 타입 파라미터가 있는데, 이것은 저장되는 객체의 타입을 List
컬렉션을 생성할 때 결정하라는 뜻 이다.
List<String> List = new ArrayList<String>();
List.add("java");// 객체 추가
List.add("JDBC");
List.add("Servlet/JSP");
List.add(2, "Database");// 지정되 인덱스에 객체 삽입
List.add("iBATIS");
String str = list.get(1); // 인덱스로 객체 검색
list.remove(0); // 인덱스로 객체 삭제
list.remove("신용권"); // 객체 삭제
객체를 추가하면 0번 인덱스부터 차례대로 저장되지만 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스
까지 모두 앞으로 1씩 당겨진다. 마찬가지로 특정 인덱스에 객체를 삽입하면 해당 인덱스부터 마지막 인덱스까지 모두 1씩
밀려나게 된다.
Vector
Vector는 ArrayList와 동일한 내부 구조를 가지고 있다. ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어
있기 때문에 멀티 스레드가 동시에 Vector의 메소드들을 실행할 수 없고, 하나의 스레드가 메소드를 실행을 완료해야만
프로세스내부에서 코드의 실행 흐름을 스레드(thread)라고 한다. 운영체제에서는 실행 중인 하나의 애플리케이션을
프로세스라고 부른다.
스레드
운영체제는 두 가지 이상의 작업을 동시에 처리하는 멀티 태스킹을 할 수 있도록 CPU 및 메모리 자원을 프로세스마다
적절히 할당해주고, 병렬로 실행시킨다. 예를 들어 워드로 문서 작업을 하면서 동시에 윈도우 미디어 플레이어로 음악을
들을 수 있다.
메인 스레드
자바의 모든 애플리케이션은 메인 스레드가 main() 메소드를 실행하면서 시작한다. 메인 스레드는 main() 메소드의
첫 코드부터 아래로 순차적으로 실행, main() 메소드의 마지막 코드를 실행하거나 return문을 만나면 실행이 종료
싱글 스레드 애플리케이션에서는 메인 스레드가 종료하면 프로세스도 종료된다. 멀티 스레드 애플리케이션에서는
실행 중인 스레드가 하나라도 있다면, 프로세스는 종료되지 않는다. 메인 스레드가 작업 스레드보다 먼저 종료되더라도
작업 스레드가 계속 실행 중이라면 프로세스는 종료되지 않는다.
작업 스레드 생성과 실행
멀티 스레드로 실행하는 애플리케이션을 개발하려면 먼저 몇 개의 작업을 병렬로 실행할지 결정하고 각 작업별로
스레드를 생성해야 한다.
어떤 자바 애플리케이션이건 메인 스레드는 반드시 존재하기 때문에 메인 작업 이외에 추가적인 병렬 작업의 수만큼
스레드를 생성하면 된다. 자바에서는 작업 스레드도 객체로 생성되기 때문에 클래스가 필요하다.
Thread 클래스로부터 직접 생성
java.lang.Thread 클래스로부터 작업 스레드 객체를 직접 생성하려면 밑에 처럼 Runnable을 매개값으로 갖는 생성자를호출해야 한다.
Thread th = new Thread(new Runnable target);
Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체라고 해서 붙여진 이름이다.
Runnable은 인터페이스 타입이기 때문에 구현 객체를 만들어 대입해야 한다. Runnable에는 run()메소드 하나가 정의
되어 있는데, 구현 클래스는 run()을 재정즤해서 작업 스레드가 실행할 코드를 작성해야 한다.
package sec01.exam01;
public class Task implements Runnable {
@Override
public void run() {
스레드가 실행할 코드;
}
}
Runnable은 작업 내용을 가지고 있는 객체이지 실제 스레드는 아니다. Runnable 구현 객체를 생성한 후, 이것을
매개값으로 해서 Thread 생성자를 호출해야 비로소 작업 스레드가 생성된다.
둘 다 동일
package sec01.exam01;
public class TaskEx {
public static void main(String[] args) {
// Runnable task = new Task();
// Thread th = new Thread(new Task());
// th.start(); // 스레드 실행 요청
// 익명의 구현 객체 ( 해당 방법이 더 많이 사용 됨 )
Thread th = new Thread(new Runnable() {
@Override
public void run() {
// 스레드가 실행할 코드
}
});
}
}
작업 스레드는 생성되는 즉시 싱행되는 것이 아니라, start( ) 메소드를 다음과 같이 호출해야만 비로소 실행 된다.
thread.start();
start( ) 메소드가 호출되면, 작업 스레드는 매개값으로 받은 Runnable의 run( ) 메소드를 실행하면서 자신의 작업을
처리합니다.
인터페이스 정의
package sec01.exam01;
import java.awt.Toolkit;
// 비프음을 들려주는 작업 정의
public class BeepTask implements Runnable { // 인터페이스
@Override
public void run() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i=0; i<5; i++) {
toolkit.beep();
try {
Thread.sleep(500);// 1000 ->1초
System.out.println("beeptask");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
상속 정의
package sec01.exam01;
// Thread 하위 클래스로부터 생성
public class BeepThread extends Thread { // 상속
@Override
public void run() {
for(int i=0; i<5; i++) {
System.out.println("BeepThread");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
실행
package sec01.exam01;
public class BeePrintEx {
public static void main(String[] args) {
// 스레드 생성, 실행
Runnable beepTask = new BeepTask();// 비프음을 들려주는 작업 정의 호출
Thread thread = new BeepThread();
thread.start();
for(int i=0; i<5; i++) {
System.out.println("띵");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
시간 스레드
정의
package sec01.exam01;
import java.time.LocalDate;
import java.time.LocalTime;
public class Time extends Thread {
@Override
public void run() {
while (true) {
String str = LocalDate.now() + " " + LocalTime.now().toString().substring(0, 8);
System.out.println(str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
실행
package sec01.exam01;
public class TaskEx {
public static void main(String[] args) {
// Runnable task = new Task();
// Thread th = new Thread(new Task());
// th.start(); // 스레드 실행 요청
// 익명의 객체 생성
Thread th = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
}
}
<결과>
2023-06-20 17:30:23
2023-06-20 17:30:24
2023-06-20 17:30:25
2023-06-20 17:30:26
2023-06-20 17:30:27
2023-06-20 17:30:28
2023-06-20 17:30:29
2023-06-20 17:30:30
...
현재시간 무한 반복
스레드의 이름
메인 스레드는 'main'이라는 이름을 가지고 있고, 우리가 직접 생성한 스레드는 자동적으로 'Thread-n'이라는 이름으로
설정 된다. n은 스레드의 번호, 다른 이름으로 설정하고 싶다면 Thread 클래스의 setName( ) 메소드로 변경하면 된다.
thread.setName("스레드 이름");
반대로 스레드 이름을 알고 싶을 경우에는 getName( ) 메소드로 변경하면 된다.
thread.getName();
해당 두개는 Thread 클래스의 인스턴스 메소드이므로 스레드 객체의 참조가 필요하다. 만약 스레드 객체의 참조를 가지고
있지 않다면, Thread 클래스의 정적 메소드인 currentThread( )를 이용해서 현재 스레드의 참조를 얻을 수 있다.
Thread th = Thread.currentThread();
ThreadA클래스
package sec01.exam02;
public class ThreadA extends Thread {
public ThreadA() {
setName("ThreadA");
}
public void run() {
for(int i=0; i<2; i++) {
System.out.println(getName() + "가 출력한 내용");
}
}
}
ThreadB클래스
package sec01.exam02;
public class ThreadB extends Thread {
@Override
public void run() {
for(int i=0; i<2; i++) {
// 스레드 이름 얻기
System.out.println(getName() + "가 출력한 내용");// <= ThreadB 실행 내용
}
}
}
메인 스레드 이름 출력 및 UserThread 생성 및 시작
package sec01.exam02;
public class ThreadNameEx {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread(); // 이 코드를 실행하는 스레드 객체 얻기
System.out.println("프로그램 시작 스레드 이름= " + mainThread.getName());
ThreadA threadA = new ThreadA();
System.out.println("작업 스레드 이름: " + threadA.getName());
threadA.start();
ThreadB threadB = new ThreadB();
System.out.println("작업 스레드 이름: " + threadB.getName());
threadB.start();
}
}
<결과>
프로그램 시작 스레드 이름= main
작업 스레드 이름: ThreadA
ThreadA가 출력한 내용
ThreadA가 출력한 내용
작업 스레드 이름: Thread-1
Thread-1가 출력한 내용
Thread-1가 출력한 내용
동기화 메소드
싱글 스레드 프로그램에서는 1개의 스레드가 객체를 독차지해서 사용하면 되지만, 멀티 스레드 프로그램에서는 스레드
들이 객체를 공유해서 작업해야 하는 경우가 있습니다. 이 경우 주의해야 할 점이 있다.
공유 객체를 사용할 때의 주의할 점
멀티 스레드 프로그램에서 스레드들이 객체를 공유해서 작업해야 하는 경우, 스레드 A가 사용하던 객체를 스레드 B가
상태를 변경할 수 있기 때문에 스레드 A가 의도했던 것과는 다른 결과를 산출할 수도 있다.
공유 객체
package sec01.exam03;
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
// public void setMemory(int memory) {
// this.memory = memory;
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + ":" + this.memory);
// }
// 임계 영역
public synchronized void setMemory(int memory) {
this.memory = memory;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + this.memory);
}
}
User1 스레드
package sec01.exam03;
public class User1 extends Thread {
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.setName("User1");
this.calculator = calculator;
}
public void run() {
calculator.setMemory(100);
}
}
User2 스레드
package sec01.exam03;
public class User2 extends Thread {
private Calculator calculator;
public void setCalculator(Calculator calculator) {
this.setName("User2");
this.calculator = calculator;
}
public void run() {
calculator.setMemory(50);
}
}
실행
package sec01.exam03;
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + this.memory);
}
}
<결과>
User2:50
User1:50
동기화 메소드
스레드가 사용 중인 객체를 다른 스레드가 변경할 수 없게 하려면 스레드 작업이 끝날 때까지 객체에 잠금을 걸어서
다른 스레드가 사용할 수 없도록 해야한다. 멀티 스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을
임계 영역이라고 한다. 자바는 임계 영역을 지정하기 위해 동기화 메소드를 제공한다. 스레드가 객체 내부의 동기화
메소드를 실행하면 즉시 객체에 잠금을 걸어 다른 스레드가 동기화 메소드를 실행하지 못하도록 한다.
동기화 메소드를 만들려면 아래 처럼 메소드 선언에 synchronized 키워드를 붙이면 되는데, 인스턴스와 정적 메소드
어디든 붙일 수 있다.
public synchronized void method() {
임계 영역; // 단 하나의 스레드만 실행
}
위에 코드에서 Calculator만 변경
package sec01.exam03;
public class Calculator {
private int memory;
public int getMemory() {
return memory;
}
// 임계 영역 ( 스레드 작업이 끝날 때까지 객체에 잠금을 걸음 )
public synchronized void setMemory(int memory) {
this.memory = memory;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + this.memory);
}
}
<결과>
User1:100
User2:50
확인 문제 ( 동영상과 음악을 재생하기 위해 두 가지 스레드 실행 )
동영상 스레드 정의
package sec01.exam04;
public class MovieThread extends Thread {
@Override
public void run() {
for(int i=0; i<3; i++) {
System.out.println("동영상을 재생합니다.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
음악 스레드 정의
package sec01.exam04;
public class MusicRunnable extends Thread {
@Override
public void run() {
for(int i=0; i<3; i++) {
System.out.println("음악을 재생합니다.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
실행
package sec01.exam04;
public class ThreadEx {
public static void main(String[] args) {
Thread thread1 = new MovieThread();
thread1.start();
Thread thread2 = new Thread(new MusicRunnable());
thread2.start();
}
}
<결과>
동영상을 재생합니다.
음악을 재생합니다.
음악을 재생합니다.
동영상을 재생합니다.
음악을 재생합니다.
동영상을 재생합니다.