00190 20151209 자바 GoF 디자인패턴 수업 3일차 - AngryQA/blog GitHub Wiki
자바 GoF 디자인패턴 수업 3일차
AngryQA | 2015-12-09 수요일 오전 10:38 | IT/개발전반 | 원본
1. Clone에 관련된것..
가변객체를 가진 클래스라면 복제 구현시 별도로 복제를 수행해야함.
[#M_더보기|접기|
public class Example1 { public static void main(String[] args) { Point pos = new Point(10, 20); Unit p1 = new Unit("Tom", 42, pos); Unit p2 = p1.clone();
p1.setPos(100, 200);
System.out.println(p1);
System.out.println(p2);
} }
// 핵심 : 클래스가 가변 객체를 가지고 있다면 복제를 구현할 때, 별도로 // 복제를 수행해야 한다. class Unit implements Cloneable { private String name; private int age;
private Point position;
@Override public Unit clone() { try { // 1. 전체 필드 복제 Unit copy = (Unit) super.clone();
// 2\. 가변 필드 복제
copy.position = position.clone();
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public void setPos(int x, int y) { position.setX(x); position.setY(y); }
public Unit(String name, int age, Point pos) { this.name = name; this.age = age; this.position = pos; }
@Override public String toString() { return "Unit{" + "name='" + name + ''' + ", age=" + age + ", position=" + position + '}'; } }
class Point implements Cloneable { private int x; private int y;
public Point(int x, int y) { this.x = x; this.y = y; }
@Override public Point clone() { try { return (Point) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }
public int x() { return x; }
public int y() { return y; }
public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
@Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; }
}
_M#]
2. 가변객체! 는 다루기 어려움....
외부에서 주입하거나.. 하는게 가능하니까..
내부에 객체에 상태가 변경되지 않는게 가장 좋다
[#M_더보기|접기|
// 가변 객체는 불변 객체보다 다루기가 어렵다.
public class Example2 { public static void main(String[] args) { Point pos = new Point(10, 20); String name = "SCV"; Unit unit = new Unit(name, pos);
pos = unit.getPosition();
pos.setX(100);
pos.setY(200);
System.out.println(unit);
} }
class Unit implements Cloneable { private String name; private Point position;
@Override public Unit clone() { try { // 1. 전체 필드 복제 Unit copy = (Unit) super.clone();
// 2\. 가변 필드 복제
copy.position = position.clone();
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public void setPos(int x, int y) { position.setX(x); position.setY(y); }
public void setName(String name) { this.name = name; }
public Point getPosition() { // 2. 접근자 메소드의 참조스를 방어 복사하여 리턴해야 한다. // return position; return position.clone(); }
public Unit(String name, Point pos) { this.name = name; // this.position = pos;
// 1\. 생성자 방어 복사가 필요하다.
// : 인자의 유효성을 검사하기 전에 복사해야 한다.
this.position = pos.clone();
}
@Override public String toString() { return "Unit{" + "name='" + name + ''' + ", position=" + position + '}'; } }
class Point implements Cloneable { private int x; private int y;
public Point(int x, int y) { this.x = x; this.y = y; }
@Override public Point clone() { try { return (Point) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; }
public int x() { return x; }
public int y() { return y; }
public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
@Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; }
}
_M#]
3. 불변클래스
정의 : 클래스의 인스턴스의 내용을 절대 바꿀 수 없는 클래스이다.
-
생성 이후, 소멸시까지 절대 변경 안됨
객체원형을 수정하지 못하도록.. 잘 사용하니까...
객체를 변경메소드 제공안함
모든필드 final, 상속금지 final class
[#M_더보기|접기|
// 객체가 가질 수 있는 값마다 새로운 객체가 필요하다. // => 여러 단계에 걸쳐 계산을 해야할 때 불변 객체를 사용하면 각 계산의 // 단계마다 새로운 객체가 새롭게 생성되므로 성능이 저하된다.
// 1. 객체를 변경하는 메소드를 제공하지 않는다. // 2. 모든 필드 final // 3. 상속 금지 final class // 4. 가변 객체 참조 필드가 필요하면 사용자가 // 해당 참조를 절대 얻을 수 없도록 해야 한다.
import java.util.Arrays; import java.util.Collection; import java.util.Collections;
final class Point { private final int x; private final int y;
// 해결 방법 2. private final Integer[] PRIVATE_VALUES = {1, 2, 3, 4, 5}; public final Collection VALUES = Collections.unmodifiableCollection(Arrays.asList(PRIVATE_VALUES));
/* // 해결 방법 1. public Integer[] values() { return VALUES.clone(); } */
public Point(int x, int y) { this.x = x; this.y = y; }
@Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + ", VALUES=" + Arrays.toString(PRIVATE_VALUES) + '}'; } }
class Example3 { public static void main(String[] args) { Point pos = new Point(10, 20); // Integer[] v = pos.VALUES; // v[0] = 100;
System.out.println(pos);
} }
_M#]
4. 쓰레드 생성에 관해
서버에관해 (자바 병렬 프로그래밍이 명서)
쓰레드풀이 신기하네.. 10개 만들어놓고 쓰는거.. 자바가 제일 잘되어있음.. 그냥 제공함..
계속 생성삭제 안하고 만들어놓고 쓴다.
[#M_더보기|접기|
import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors;
public class Example4 {
/* private static void handleRequest(Socket connection) { OutputStream os = null; try { os = connection.getOutputStream(); os.write("Hello World".getBytes()); os.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (os != null) os.close(); } catch (IOException e) { e.printStackTrace(); } } } */
private static void handleRequest(Socket connection) { try (OutputStream os = connection.getOutputStream()) { os.write("Hello World".getBytes()); os.flush(); } catch (IOException e) { e.printStackTrace(); } }
// Step 3. Pool-based Model // 순차적으로 문제를 해결하는 방법은 응답속도와 전체적인 성능이 // 떨어진다는 문제가 있고, 작업마다 스레드를 만들어 내는 방법은 // 자원 관리 측면에서 문제점이 있다. public static final int NTHREAD = 10; public static final Executor exec = Executors.newFixedThreadPool(NTHREAD); // 1) newFixedThreadPool(N) // : 작업이 등록되면 제한된 개수까지 스레드를 생성해서 해당 작업을 처리한다. // 생성 이후에 더 이상 생성하지 않고, 스레드 수를 유지한다.
// 2) newCachedThreadPool(N) // : 스레드가 수가 처리할 작업보다 많아서 대기하는 스레드가 발생하면 // 스레드를 제거한다.
// 3) newSingleThreadPool() // : 단일 스레드로 처리한다. // 등록된 작업을 다양한 정책에 의해서 수행할 수 있다. // (FIFO, LIFO, 우선순위)
public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(8080);
while (true) {
Socket connection = socket.accept();
Runnable task = new Runnable() {
@Override
public void run() {
handleRequest(connection);
}
};
exec.execute(task);
}
}
/* // Step 2. Thread per Connection Model // 순차적인 실행 방법보다 훨씬 더 많은 작업을 수행하는 것이 가능하다.
// 문제점 // 1. 엄청나게 많은 스레드가 생성된다. // 2. 스레드를 제거하는 작업도 자원이 소모된다. // 3. 자원 낭비가 심하다. // 1) 실행 중인 스레드는 메모리를 많이 소모한다. // 자바의 스택은 두 개이다.(Java, Native) // 2) 프로세서의 개수보다 더 많은 스레드가 생성되면, // 대부분의 스레드는 대기 상태이다. // 4. 컨텍스트 스위칭의 비용도 크다. // 5. OS 에서 생성할 수 있는 스레드 개수도 제한되어 있다. public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(8080); while (true) { Socket connection = socket.accept(); Runnable task = new Runnable() { @Override public void run() { handleRequest(connection); } };
new Thread(task).start();
}
} */
/* // Step 1. Single Thread Modle // 1) 한번에 하나의 요청을 처리하는 것이 가능하다. // 2) 하드웨어 자원을 효과적으로 사용할 수 없다. public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(8080);
while (true) {
Socket connection = socket.accept();
handleRequest(connection);
}
} */ }
_M#]
5. 쓰레드의 중단 및 종료처리
Volatile 타입!! 컴파일러에게 최적화 하지말라는 지시어
메모리 가시성때문에 그렇슴.. 캐쉬에 올려놓고 하니까.. CPU많으면 캐쉬는 다 따로 다로 니까..
[#M_더보기|접기|
import java.util.concurrent.TimeUnit;
// 스레드의 중단 및 종료 처리 // 1. 스레드를 만들고 시작하는 것은 쉬운 일이다. // 2. 스레드를 안전하고 빠르게 멈추는 것은 어렵다. // 3. 스레드를 멈추는 메소드는 폐기되었다. // Thread.stop() / Thread.suspend() // 4. 스레드를 강제로 종료하면 공유되어 있는 다양한 상태가 // 망가질 수 있다. // 5. 스레드를 멈추어 달라는 요청이 오면 진행중이던 모든 작업을 // 스스로 정리한 후 종료하도록 구현해야 한다.
// 문제점 : 메인 스레드가 변경한 stopRequested의 새로운 값을 // 다른 스레드에서 관측할 수 없다.
// 해결책 1. 동기화(synchronization) // 1) 상호 배제 : 다른 스레드가 변경 중인 객체의 상태를 관측할 수 없도록 // 해준다 -> 락 // 2) 동기화는 동기화 메소드 또는 동기화 블록에 의해 진입한 스레드가 동일한 // 락의 보호 아래 이루어진 모든 변경을 관측할 수 있도록 해준다.
// 3) 순환문의 각 단계마다 동기화를 실행하는 비용이 크다. // => 성능의 저하가 있다. /* public class Example5 { private static boolean stopRequested = false;
private static synchronized void stop() { stopRequested = true; }
private static synchronized boolean isStopRequested() { return stopRequested; }
public static void main(String[] args) throws Exception { Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { long i = 0; while (!isStopRequested()) { i++; // System.out.println(i); } } });
backgroundThread.start();
TimeUnit.SECONDS.sleep(3);
stop();
}
} */
// 해결책 2. volatile // 정의 : 컴파일러에게 최적화를 하지 말라는 지시어 // 멀티 스레드 상에서 공유되는 변수, 하드웨어와 연결된 변수 사용시 // volatile 을 붙여야 한다 // 효과 : 어떤 스레드건 최근에 기록된 값을 읽을 수 있다. public class Example5 { private static volatile boolean stopRequested = false;
public static void main(String[] args) throws Exception { Thread backgroundThread = new Thread(new Runnable() { @Override public void run() { long i = 0; while (stopRequested) { i++; // System.out.println(i); } } });
backgroundThread.start();
TimeUnit.SECONDS.sleep(3);
stopRequested = true;
}
}
_M#]
6. 가짜공유로 성능저하 가능성있음
그래서 패딩을 준다...
[#M_더보기|접기|
import org.junit.Test;
public class Example6 { //static void main(String[] args) throws InterruptedException {
@Test public void perfTest() throws Exception { Thread[] threads = new Thread[C.NUM_THREADS]; for (int i = 0; i < threads.length; ++i) { threads[i] = new Task(i); }
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
} }
class C { public final static int NUM_THREADS = 8; public final static long COUNT = 5000L * 1000L * 1000L; }
// 가짜 공유 문제(False Sharing) // 개념 : 두 스레드간의 공유가 발생하지 않음에도, // 캐시 라인의 공유에 의해서 서로의 캐시를 무효화함으로 // 성능이 저하되는 문제.
class Task extends Thread { static class Value { public long value = 0L; // public long p1, p2, p3, p4, p5, p6; // 패딩 구문 - 이게 있어야 캐쉬초기화를 하지 않는다. CPU가 같은 캐쉬메모리를 참조하는경우) }
private static Value[] longs = new Value[C.NUM_THREADS];
static { for (int i = 0; i < longs.length; ++i) { longs[i] = new Value(); } }
private int index;
public Task(int index) { this.index = index; }
@Override public void run() { long i = C.COUNT + 1; while (0 != --i) longs[index].value = i; }
}
_M#]
7. CPU에 따른 최적화에 대한 얘기
어토믹인트 - 동기화는해결했지만 CPU를 멈추니까.. 병렬화를 못함
따로 돌고 취합하게 만들면 빠르다아.. 설정에따라 속도가 차이가난다..
쓰레드는 운영체제입장이고... 사용자는 task(작업)...로 생각하면 좋다..트랜잭션.... 그럼 확장성...
[#M_더보기|접기|
// 1억 만들기 // 멀티 스레드 모델 : 확장성
import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
// http://d.pr/n/13XdY public class Example7 { private static int value = 0; public static final int THREAD_COUNT = 4;
private static final Runnable TASK1 = new Runnable() { @Override public void run() { for (int i = 0 ; i < 500000000 / THREAD_COUNT ; i++) { value += 2; // mov eax, value // add eax, 2 // mov value, eax } } };
private static final Runnable TASK2 = new Runnable() { @Override public void run() { for (int i = 0 ; i < 50000000 / THREAD_COUNT ; i++) { synchronized (Example7.class) { value += 2; }
}
}
};
private static AtomicInteger atomicInteger = new AtomicInteger(0); private static final Runnable TASK3 = new Runnable() { @Override public void run() { for (int i = 0 ; i < 50000000 / THREAD_COUNT ; i++) { atomicInteger.addAndGet(2); } } };
private static AtomicInteger atomicInteger2 = new AtomicInteger(0); private static final Runnable TASK4 = new Runnable() { @Override public void run() { int localSum = 0; for (int i = 0 ; i < 50000000 / THREAD_COUNT ; i++) { localSum += 2; }
atomicInteger2.addAndGet(localSum);
}
};
// SingleThread : 18ms @Test public void perf1() throws Exception { value = 0;
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0 ; i < threads.length ; ++i)
threads[i] = new Thread(TASK1);
for (int i = 0 ; i < threads.length ; ++i)
threads[i].start();
for (int i = 0 ; i < threads.length ; ++i)
threads[i].join();
System.out.println(value);
}
@Test public void perf2() throws Exception { value = 0;
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0 ; i < threads.length ; ++i)
threads[i] = new Thread(TASK2);
for (int i = 0 ; i < threads.length ; ++i)
threads[i].start();
for (int i = 0 ; i < threads.length ; ++i)
threads[i].join();
System.out.println(value);
}
@Test public void perf3() throws Exception { value = 0;
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0 ; i < threads.length ; ++i)
threads[i] = new Thread(TASK3);
for (int i = 0 ; i < threads.length ; ++i)
threads[i].start();
for (int i = 0 ; i < threads.length ; ++i)
threads[i].join();
System.out.println(atomicInteger);
}
@Test public void perf4() throws Exception { value = 0;
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0 ; i < threads.length ; ++i)
threads[i] = new Thread(TASK4);
for (int i = 0 ; i < threads.length ; ++i)
threads[i].start();
for (int i = 0 ; i < threads.length ; ++i)
threads[i].join();
System.out.println(atomicInteger);
} }
실행결과
_M#]
8. 해쉬맵 동기화 및 쓰레드에 관해
Lock-free, Concurrent Collection
컬렉스 자료형은 락프리다!
[#M_더보기|접기|
import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;
public class Example9 { public final static int THREAD_POOL_SIZE = 16;
public static Map hashTableObject = null;
public static Map synchronizedMapObject = null;
public static Map concurrentHashMapObject = null;
public static void main(String[] args) throws InterruptedException {
hashTableObject = new Hashtable();
performTest(hashTableObject);
synchronizedMapObject = Collections.synchronizedMap(new HashMap());
performTest(synchronizedMapObject);
concurrentHashMapObject = new ConcurrentHashMap();
performTest(concurrentHashMapObject);
}
public static void performTest(final Map map) throws InterruptedException {
System.out.println("Test started for: " + map.getClass());
long averageTime = 0;
for (int i = 0; i < 5; i++) {
long startTime = System.nanoTime();
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (int j = 0; j < THREAD_POOL_SIZE; j++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 500000; i++) {
Integer randomNumber = Integer.valueOf((int) Math.random());
Integer value = map.get(String.valueOf(randomNumber));
map.put(String.valueOf(randomNumber), randomNumber);
}
}
});
}
// Make sure executor stops
executorService.shutdown();
// Blocks until all tasks have completed execution after a shutdown request
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
long entTime = System.nanoTime();
long totalTime = (entTime - startTime) / 1000000L;
averageTime += totalTime;
System.out.println("500K entried added/retrieved in " + totalTime + " ms");
}
System.out.println("For " + map.getClass() + " the average time is " + averageTime / 5 + " ms\n");
}
}
실행결과
_M#]
오늘 수업 끝.