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#]

오늘 수업 끝.

20151209.zip

Attachments(1)