Главная » Java, Web » Синхронизация

0

В сервере connect ionBroker существует момент, обойти вниманием который мы не сможем. Что произойдет, если два или более потоков обратятся к одним и тем же данным? В этом случае данные могут быть неверно использованы. Чтобы такого не произошло, используется синхронизация. Предположим, что количество денег на счету в банке представлено в виде следующего класса: public class BankAccount {

private double balance; // сумма на счету public double getBalance() { return balance;

}

public void withdraw(double amount) {

// условие: баланс balance должен быть больше или равен сумме amount balance = balance — amount;

}

// прочие методы

}

Предположим, что account — это объект типа BankAccount, и эта переменная используется сразу в нескольких потоках. Предположим, что один из потоков желает снять со счета $100:

if (account.getBalance() >= 100) account.withdraw();

Теперь представим, что не только этот поток стремится снять суммы со счета, а сразу два потока пытаются снять указанную сумму. Если на счету есть 150 долларов и оба потока действуют в одно и то же время, то в принципе каждый из них может получить по 100 долларов, и на счету еще останется $50. Работа потоков будет происходить примерно в такой последовательности:

1.      Первый поток читает баланс и получает значение $150.

2.       Второй поток читает баланс и получает значение $150.

3.       Второй поток снимает $100, всего остается $50.

4.       Второй поток сохраняет новое значение баланса, то есть $50.

5.     Первый поток вычитает $100 из $150, остается $50.

6.     Первый поток сохраняет новое значение баланса счета $50. Вряд ли банку "понравится" такое программирование.

Можно подумать, что такая ситуация маловероятна. И пусть довольно редко, но она может возникнуть. В конце концов она обязательно произойдет. Поэтому синхронизация просто необходима, и с ее помощью решается задача доступа к общим данным.

Библиотека swing работает с синхронизацией весьма прямолинейно. В пакете Swing только один поток может работать с данными, используемыми компонентом Swing. Если другой поток попробует сделать что-либо с данными, то ему это просто не будет позволено. Но swing обладает методами

SwingUtilities.invokeLater() И SwingUtilities.invokeAndWait(), С ИХ помощью доступ может быть отложен на некоторое время и осуществлен позже. Именно такой тип синхронизации используется в классах

ChatSimulation, ChatWindow, BrokeredChat.

Во многих случаях метод синхронизации, используемый в Swing, оказывается неприемлемым. Существует более общий способ синхронизации в целях контроля за доступом к общим данным. Для этого используется инструкция

synchronized. Например: synchronized (<object-reference>) { <statements>

}

В нашем случае можно было написать следующее:

synchronized(account) {

if (account.getBalance() >= amount) balance = balance — amount;

}

Идея состоит в том, что <object-reference> становится как бы запертым на время выполнения инструкций, помещенных в блок synchronized. В нашем случае все методы объекта account будут блокированы до тех пор, пока весь блок synchronized не будет выполнен. Это значит, что два потока не смогут одновременно обратиться к банковскому счету.

Один и тот же объект может быть использован в нескольких блоках synchronized, но только один из блоков инструкций будет выполняться в каждый момент времени. Программа connectionBroker работает с потоками, которые имеют доступ к одним и тем же данным, например, к списку участников чата, который представлен в виде вектора с именем clientList. Этот вектор используется многими потоками. С помощью нижеприведенного кода (листинг 2.15) происходит обращение к списку пользователей clientList (синхронизация выделена полужирным шрифтом).

Листинг 2.15. Пример использования синхронизации

доступа к данным типа Vector с именем clientList. Список содержит участников чата. также защищает переменную nextClientlnfo.

static void addClient(Client client) { // добавляем нового клиента в clientList synchronized(clientList) {

client.ID = nextClientID++; if (client.info.length() == 0)

client.info = "Anonymous" + client.ID; clientList.addElement(client);

System.out.println("Added client " + client.ID + " " + client.info);

static void removedient(Client client) { // удаляем клиента из списка clientList synchronized(clientList) {

clientList.removeElement(client) ;

System.out.println("Removed client " + client.ID);

static Client getClient(int ID) {

// Удаляем клиента из списка. Если клиент обладает указанным // ID, возвращает удаляемого клиента. В противном случае возвращается // значение null, synchronized(clientList) {

for (int i = 0; i < clientList.size(); i++) { Client с = (Client)clientList.elementAt(i); if (c.ID == ID) {

clientList.removeElementAt(i);

System.out.println("Removed client " + c.ID);

c.ID = 0; // Since this client is no longer waiting!

return c;

}

return null;

}

}

static Client[] getClients() {

// Возвращает массив клиентов из списка // clientList. Если никого нет — возвращает null, synchronized(clientList) {

if (clientList.size() == 0) return null;

Client[] clients = new Client[ clientList.size() ]; for (int i = 0; i < clientList.size(); i++)

clients[i] = (Client)clientList.elementAt(i); return clients;

}

}

Таким образом, синхронизация часто бывает весьма полезной. С ее помощью мы имеем возможность защитить ресурсы от вероятного некорректного использования.

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии