#10 effective java: item 7
This commit is contained in:
37
effective-java/src/main/java/com/example/effectivejava/chapter01/item07/cache/CacheKey.java
vendored
Normal file
37
effective-java/src/main/java/com/example/effectivejava/chapter01/item07/cache/CacheKey.java
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.cache;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class CacheKey {
|
||||||
|
|
||||||
|
private Integer value;
|
||||||
|
|
||||||
|
private LocalDateTime created;
|
||||||
|
|
||||||
|
public CacheKey(Integer value) {
|
||||||
|
this.value = value;
|
||||||
|
this.created = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return this.value.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CacheKey{" +
|
||||||
|
"value=" + value +
|
||||||
|
", created=" + created +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
39
effective-java/src/main/java/com/example/effectivejava/chapter01/item07/cache/Post.java
vendored
Normal file
39
effective-java/src/main/java/com/example/effectivejava/chapter01/item07/cache/Post.java
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.cache;
|
||||||
|
|
||||||
|
public class Post {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finalize() {
|
||||||
|
System.out.println("gc called");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.cache;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class PostRepository {
|
||||||
|
|
||||||
|
private Map<CacheKey, Post> cache;
|
||||||
|
|
||||||
|
public PostRepository() {
|
||||||
|
this.cache = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Post getPostById(CacheKey key) {
|
||||||
|
if (cache.containsKey(key)) {
|
||||||
|
return cache.get(key);
|
||||||
|
} else {
|
||||||
|
// TODO DB에서 읽어오거나 REST API를 통해 읽어올 수 있습니다.
|
||||||
|
Post post = new Post();
|
||||||
|
cache.put(key, post);
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<CacheKey, Post> getCache() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.listener;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ChatRoom {
|
||||||
|
|
||||||
|
private List<WeakReference<User>> users;
|
||||||
|
|
||||||
|
public ChatRoom() {
|
||||||
|
this.users = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUser(User user) {
|
||||||
|
this.users.add(new WeakReference<>(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(String message) {
|
||||||
|
users.forEach(wr -> Objects.requireNonNull(wr.get()).receive(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WeakReference<User>> getUsers() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.listener;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
public void receive(String message) {
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.stack;
|
||||||
|
|
||||||
|
// (36쪽의 Stack 코드에서 던지는 예외)
|
||||||
|
public class EmptyStackException extends IllegalStateException {
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.stack;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// 코드 7-1 메모리 누수가 일어나는 위치는 어디인가? (36쪽)
|
||||||
|
public class Stack {
|
||||||
|
private Object[] elements;
|
||||||
|
private int size = 0;
|
||||||
|
private static final int DEFAULT_INITIAL_CAPACITY = 16;
|
||||||
|
|
||||||
|
public Stack() {
|
||||||
|
elements = new Object[DEFAULT_INITIAL_CAPACITY];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(Object e) {
|
||||||
|
ensureCapacity();
|
||||||
|
elements[size++] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Object pop() {
|
||||||
|
// if (size == 0)
|
||||||
|
// throw new EmptyStackException();
|
||||||
|
// return elements[--size];
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 원소를 위한 공간을 적어도 하나 이상 확보한다.
|
||||||
|
* 배열 크기를 늘려야 할 때마다 대략 두 배씩 늘린다.
|
||||||
|
*/
|
||||||
|
private void ensureCapacity() {
|
||||||
|
if (elements.length == size)
|
||||||
|
elements = Arrays.copyOf(elements, 2 * size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
|
||||||
|
public Object pop() {
|
||||||
|
if (size == 0)
|
||||||
|
throw new EmptyStackException();
|
||||||
|
Object result = elements[--size];
|
||||||
|
elements[size] = null; // 다 쓴 참조 해제
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Stack stack = new Stack();
|
||||||
|
for (String arg : args)
|
||||||
|
stack.push(arg);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
System.err.println(stack.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.cache;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class PostRepositoryTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cache() throws InterruptedException {
|
||||||
|
PostRepository postRepository = new PostRepository();
|
||||||
|
CacheKey key1 = new CacheKey(1);
|
||||||
|
postRepository.getPostById(key1);
|
||||||
|
|
||||||
|
assertFalse(postRepository.getCache().isEmpty());
|
||||||
|
|
||||||
|
key1 = null;
|
||||||
|
// TODO run gc
|
||||||
|
System.out.println("run gc");
|
||||||
|
System.gc();
|
||||||
|
System.out.println("wait");
|
||||||
|
Thread.sleep(3000L);
|
||||||
|
|
||||||
|
assertTrue(postRepository.getCache().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void backgroundThread() throws InterruptedException {
|
||||||
|
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||||
|
PostRepository postRepository = new PostRepository();
|
||||||
|
CacheKey key1 = new CacheKey(1);
|
||||||
|
postRepository.getPostById(key1);
|
||||||
|
|
||||||
|
Runnable removeOldCache = () -> {
|
||||||
|
System.out.println("running removeOldCache task");
|
||||||
|
Map<CacheKey, Post> cache = postRepository.getCache();
|
||||||
|
Set<CacheKey> cacheKeys = cache.keySet();
|
||||||
|
Optional<CacheKey> key = cacheKeys.stream().min(Comparator.comparing(CacheKey::getCreated));
|
||||||
|
key.ifPresent((k) -> {
|
||||||
|
System.out.println("removing " + k);
|
||||||
|
cache.remove(k);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
System.out.println("The time is : " + new Date());
|
||||||
|
|
||||||
|
executor.scheduleAtFixedRate(removeOldCache,
|
||||||
|
1, 3, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
Thread.sleep(20000L);
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.example.effectivejava.chapter01.item07.listener;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class ChatRoomTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void charRoom() throws InterruptedException {
|
||||||
|
ChatRoom chatRoom = new ChatRoom();
|
||||||
|
User user1 = new User();
|
||||||
|
User user2 = new User();
|
||||||
|
|
||||||
|
chatRoom.addUser(user1);
|
||||||
|
chatRoom.addUser(user2);
|
||||||
|
|
||||||
|
chatRoom.sendMessage("hello");
|
||||||
|
|
||||||
|
user1 = null;
|
||||||
|
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
Thread.sleep(5000L);
|
||||||
|
|
||||||
|
List<WeakReference<User>> users = chatRoom.getUsers();
|
||||||
|
assertEquals(1, users.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user