PainterKarelProgram2 (KarelOOP2)
Retired DISLab
/* * Copyright 2020 Sangwon Park @ DISLab, HUFS * * 10명의 페이트 칠쟁이들이 무작위로 돌아다니며 바닥을 칠하는 프로그램이다. * * 서로 부딪혀서 캐럴이 망가지지 않도록 하기 위하여 lock을 준비하였다. * 이 lock들은 코너 마다 하나씩 준비되어 있고, 좌표를 이용하여 락을 획득한다. * * 락을 거는 프로토콜은 다음과 같다. * 1. 앞으로 전진할 위치의 락을 획득한다. * 2. 캐럴을 앞으로 한 칸 전진한다. * 3. 이전 위치의 락을 해제한다. * * 이때 데드락(deadlock)이 발생한다. 보편적으로 락을 이용하면 데드락이 발생할 수 있다. * 데드락이 발생하는 경우는 다음과 같다. * 1. 예를 들어 두 개의 캐럴 K1, K2가 서로 마주보고 있을 때 서로 상대 위치를 탐할 때 * 2. 이미 데드락이 걸려 움직일 수 없는 캐럴이 있는 위치에 대한 락을 요구할 때 * * 즉 데드락은 현재의 스레드가 락을 쥐고 있으면 다른 락을 요청할 때 발생할 수 있다. * * 시간이 지나면 모든 캐럴이 상대가 가진 락을 무한정 기다리는 상태가 되어 프로그램이 멈춘다. */ package cp.java.week11.thread; import java.awt.Color; import java.awt.Point; import java.util.Random; import hufs.dislab.karel.IKarelProgram; class LockManager { private LockItem[][] table; private class LockItem { private boolean value = false; public synchronized void lock() { while (value == true) { try { wait(); } catch (InterruptedException e) { } } value = true; notifyAll(); } public synchronized void unlock() { value = false; notifyAll(); } } public LockManager(int width, int height) { table = new LockItem[width][height]; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) table[i][j] = new LockItem(); } } public void lock(int x, int y) { //String name = Thread.currentThread().getName(); //System.out.println(name + ": lock " + x + "," + y); table[x - 1][y - 1].lock(); } public void unlock(int x, int y) { //String name = Thread.currentThread().getName(); //System.out.println(name + ":unlock " + x + "," + y); table[x - 1][y - 1].unlock(); } public boolean peek(int x, int y) { return table[x - 1][y - 1].value; } } /** * DEADLOCK 발생함. 서로 마주보며 서로의 위치에 대한 Lock을 요청하면 데드락이 발생함. * @author Sangwon Park * @since KarelOOP 2 */ @SuppressWarnings("serial") public class PainterThreadProgram2 extends IKarelProgram { public static final int NO_KARELS = 10; MyKarel[] list = new MyKarel[NO_KARELS]; LockManager lmgr; class Crazy extends Thread { MyKarel karel; Random r = new Random(); Crazy(MyKarel karel) { this.karel = karel; } @Override public void run() { while (true) { switch (r.nextInt(4)) { case 0: karel.turnLeft(); break; case 1: karel.turnRight(); break; case 2: karel.turnAround(); break; } if (karel.frontIsClear()) { Point pt = karel.getLocation(); Point nextpt = new Point(pt); if (karel.facingEast()) nextpt.x++; else if (karel.facingWest()) nextpt.x--; else if (karel.facingNorth()) nextpt.y++; else nextpt.y--; lmgr.lock(nextpt.x, nextpt.y); karel.move(); karel.paintCorner(); lmgr.unlock(pt.x, pt.y); } } } } static Color[] colors = new Color[] { BLUE, RED, YELLOW, ORANGE, CYAN, GREEN, GRAY, PINK, MAGENTA, LIGHT_GRAY, BLACK, DARK_GRAY, WHITE}; @Override protected void onInit() { int width = getWorldWidth(); int height = getWorldHeight(); lmgr = new LockManager(width, height); list[0] = (MyKarel) getKarel(); Point pt = list[0].getLocation(); lmgr.lock(pt.x, pt.y); Random r = new Random(); for (int i = 1; i < list.length; i++) { int x, y; do { x = r.nextInt(width) + 1; y = r.nextInt(height) + 1; } while (lmgr.peek(x, y)); lmgr.lock(x, y); int dir = r.nextInt(4); Color color = colors[i % colors.length]; MyKarel karel = new MyKarel(x, y, dir, color); list[i] = karel; addIKarel(list[i]); } } @Override public void onStart() { for (MyKarel karel : list) { new Crazy(karel).start(); } } public static void main(String[] args) { IKarelProgram.main(args, new MyKarel(1, 1, EAST, BLUE)); } }