воскресенье, 25 октября 2009 г.

Example: Threads ping-pongs

В категории [Example] я буду публиковать небольшие ПОЛНОСТЬЮ ЗАКОНЧЕННЫЕ многопоточные приложения. Я их буду публиковать с импортами, так что их достаточно просто ввести в одно окно IDE и запустить. По причине "одного окна" один из классов будет public, остальные с default областью видимости. Естественно, если Вы каждый класс будете хранить в отдельном файле (не в "одном окне"), то можете все классы делать public.

Сегодняшний пример - два потока перебрасывающиеся объектом.
----------

Следственный эксперимент: класс PingPong запускает два класса Player, а они перебрасываются классом Item. Item содержит простой счетчик типа int, считающий количество передач. Передачи осуществляются через механизм wait()/notify().
На тестовом компьютере (AMD Duron 1300, WinXP, jdk 1.6_13) 100.000.000 передач было выполнено за 232с, т.е. на одно переключение контекста уходило в среднем 232 * 10 * 1.3 = 3016 тактов. В примере я сократил 100.000.000 до 1.000.000.

-------------------------- start PingPong.java file
import java.util.concurrent.CountDownLatch;

public class PingPong {
// constant
public static final int ITERATION_COUNT = 1000 * 1000;
// global variables :(
public static final Item item = new Item();
public static final CountDownLatch start = new CountDownLatch(1);
public static final CountDownLatch stop = new CountDownLatch(2);

public static void main(String[] args) throws InterruptedException {
// init section
new Thread(new Player(false)).start(); //player #0
new Thread(new Player(true)).start(); //player #1

// work section
long startTime = System.nanoTime();
start.countDown();
stop.await();
long deltaTime = System.nanoTime() - startTime;

// info-to-console section
System.out.println("" + ITERATION_COUNT + " transfers take " + deltaTime + " nanosecs");
}
}

class Item {
int counter = 0;
}

class Player implements Runnable {
private final boolean imOdd;

public Player(boolean imOdd) {
this.imOdd = imOdd;
}

public void run() {
// await for start ping-ponging
try {
PingPong.start.await();
} catch (InterruptedException e) {
e.printStackTrace();
}

synchronized (PingPong.item) {
while (true) {
// wait for my turn (avoid spirious wakeup)
while (((PingPong.item.counter & 1) == 1) == imOdd) {
try {
PingPong.item.wait(); // wait for "Ping"
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// increment Ping-Poin counter
PingPong.item.counter++;

// do "Pong"
PingPong.item.notify();

if (PingPong.item.counter >= PingPong.ITERATION_COUNT) {
PingPong.stop.countDown(); // show for PingPoint.class that my work doing
return; // break, thats all
}
}
}
}
}
-------------------------- end PingPong.java file


Замечания:
- пуск и ожидание останова двух потоков делается с использованием java.util.concurrent.CountDownLatch. Это одна из "законных" функций этого класса.
- я довольно небрежно работаю с InterruptedException так как цель - просто демонстрация
- в классе PingPong три глобальных переменных, это плохо, но для демонстрации - сгодится
- в данном примере всего два игрока и так легко его не приспособишь для большего количества (так как в Player.java поле типа boolean имеет всего два значения (четный - нечетный))
------------------

Задачка - 1: сделать 100 потоков перебрасывающихся по кругу одним Item.
Задачка - 2: сделать 100 потоков перебрасывающихся по кругу одновременно 10 Item.

Комментариев нет:

Отправить комментарий