Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Postgresql + синхронизация при многопотоке 
:(
    Опции темы
pinansonoyon
Дата 29.12.2009, 23:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 30.8.2009

Репутация: нет
Всего: нет



Привет, люди планеты Земля!

Появилась такая задача: есть базейка. В ней есть уникальный ключ, некий адрес клиента и поле для проверки (чего- станет ясно позднее). Создавалась она так:

Код

CREATE TABLE main_ADDRs (unique_key TEXT, ADDRs TEXT, plus_minus_ADDRs TEXT);


И вот я подумал- а что если адреса клиентов обрабатывать в потоках? Но ведь потоки ломанутся в базу и возможна ситуация, при которой несколько потоков будут
обрабатывать один и тот же адрес! Тогда я решил обмануть весь мир и сделать функцию в базе, которая бы блокировала базу, ставила бы в кортеже минусик, что показывало бы другим потокам, что адрес обрабатывается и его трогать не стоит. Родилась такая функция:

Код

CREATE LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION main_ADDRs_plus_minus_adding (unique_key_4_function TEXT)
RETURNS TEXT AS $$
DECLARE
itog_of_function TEXT;
UNIQUE_KEY TEXT;
BEGIN
LOCK TABLE main_ADDRs IN SHARE MODE;
UNIQUE_KEY = unique_key_4_function;
IF(SELECT COUNT(unique_key) AS main_unique_key FROM main_ADDRs WHERE  plus_minus_ADDRs = NULL) = 0
THEN
UPDATE main_ADDRs SET  plus_minus_ADDRs = '-' WHERE unique_key = UNIQUE_KEY;
itog_of_function = UNIQUE_KEY;
ELSE
itog_of_function = 'no';
END IF;
RETURN itog_of_function;
END;
$$ LANGUAGE plpgsql;


Сам текст проги такой:

Код

package ADDRs;

import ADDRsDoing.ADDRsDoing;
import java.io.IOException;
import java.net.SocketException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;

class Main
{

public static void main (String args[]) throws ClassNotFoundException, SQLException, NoSuchAlgorithmException, InterruptedException, IOException, SocketException
    {
///////////////////////////////////KOLI4ESTVO POTOKOV
    int Quantity_of_Threads = 2;

    // podgotovka potokov
    Thread t[] = new Thread[Quantity_of_Threads];
    for (int i=0; i<t.length; i++) {
      t[i]=new Thread(new MAINThread());
    }
    //zapusk potokov
    for (int i=0; i<t.length; i++) {
      t[i].start();
    }
    }
}
  class MAINThread implements Runnable
{
      public MAINThread() throws ClassNotFoundException, SQLException, NoSuchAlgorithmException, InterruptedException, IOException, SocketException
      {
      }
            public void run()
    {


        ADDRsDoing AADDRsDoing = new ADDRsDoing();
        try {
            AADDRsDoing.runADDRsDoing();
        }
        catch (ClassNotFoundException ex) {}
        catch (SQLException ex) {}
    }
}


Класс по обработке адресов из базы:

Код

package ADDRsDoing;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class ADDRsDoing {

        public void runADDRsDoing() throws ClassNotFoundException, SQLException
    {
// ЗДЕСЬ МЫ ПОЛУЧАЕМ ИЗ БАЗЫ ВСЕ АДРЕСА, НЕ ИМЕЮЩИЕ НИ ПЛЮСИКОВ, НИ МИНУСИКОВ, ТО ЕСТЬ ТЕ, КОТОРЫЕ И НАДО ОБРАБОТАТЬ
  Class.forName("org.postgresql.Driver");
  Connection connect4ADDRsGetting = null;
  connect4ADDRsGetting = DriverManager.getConnection("jdbc:postgresql://localhost/BD44ADDRs?user=postgres&password=kuku");
  Statement se4ADDRsGetting = null;
  se4ADDRsGetting = connect4ADDRsGetting.createStatement();
  ResultSet rs4connect4ADDRsGetting = null;
  rs4connect4ADDRsGetting = se4ADDRsGetting.executeQuery("SELECT * FROM main_ADDRs WHERE plus_minus_ADDRs IS NULL");

  while(rs4connect4ADDRsGetting.next())
  {
// ТУТ МЫ ВСЕ ЭТО ПЕРЕБИРАЕМ И ПЫТАЕМСЯ ЗАМИНУСОВАТЬ КОРТЕЖИ, БЛОКИРУЯ БАЗУ
  String UNIQUE_KEY_of_ADDRs = rs4connect4ADDRsGetting.getString(1);
  Class.forName("org.postgresql.Driver");
  Connection connect4minus_getting = null;
  connect4minus_getting = DriverManager.getConnection("jdbc:postgresql://localhost/BD44ADDRs?user=postgres&password=kuku");
  Statement se4connect4minus_getting = null;
  se4connect4minus_getting = connect4minus_getting.createStatement();
  ResultSet rs4connect4minus_getting = null;
  rs4connect4minus_getting = se4connect4minus_getting.executeQuery("SELECT * FROM main_ADDRs_plus_minus_adding('"+UNIQUE_KEY_of_ADDRs+"')");
// ЗДЕСЬ, ПО ПЕРВОНАЧАЛЬНОМУ ЗАМЫСЛУ, МЫ ПОЛУЧАЕМ ТЕ АДРЕСА, КОТОРЫЕ НАДО ОБРАБОТАТЬ И КОТОРЫЕ НЕ ОБРАБАТЫВАЮТ ДРУГИЕ ПОТОКИ
  while(rs4connect4minus_getting.next())
  {
  String existing_or_not_4_ADDRs = rs4connect4minus_getting.getString(1);
  System.out.println(existing_or_not_4_ADDRs);
  }
  }
    }
}


Ну чего сказать? В однопотоке-то оно работало, а в многопотоке начались глюки, как и предсказывали умные люди: то работает как надо, то работает так, 
как будто не существует никакой блокировки таблицы, то работает какой-то усредненный вариант из этих двух вариантов. Залез в литературу. Нашел информацию о 
синхронизации потоков при использовании общих ресурсов. Ну, решил попробовать, хотя многие моменты остались неясными. Изменения коснулись класса Main:

Код

class Main
{

public static void main (String args[]) throws ClassNotFoundException, SQLException, NoSuchAlgorithmException, InterruptedException, IOException, SocketException
    {
///////////////////////////////////KOLI4ESTVO POTOKOV
    int Quantity_of_Threads = 2;

    // podgotovka potokov
    Thread t[] = new Thread[Quantity_of_Threads];
    for (int i=0; i<t.length; i++) {
      t[i]=new Thread(new MAINThread());
    }
    //zapusk potokov
    for (int i=0; i<t.length; i++) {
      t[i].start();
    }
    Thread.yield();
    }
}
  class MAINThread implements Runnable
{
      public MAINThread() throws ClassNotFoundException, SQLException, NoSuchAlgorithmException, InterruptedException, IOException, SocketException
      {
      }
            public void run()
    {

synchronized (this) {
        ADDRsDoing AADDRsDoing = new ADDRsDoing();
        try {
            AADDRsDoing.runADDRsDoing();
        }
        catch (ClassNotFoundException ex) {}
        catch (SQLException ex) {}
                   }
    }
}


Итог оказался точно таким же, как и до синхронизации. Те примеры, которые показывались в литературе меня убедили в том, что решение проблемы, в общем-то,
лежит в области синхронизации, но куда чего там вставить- не могу понять. Какие будут мысли и предложения? Всех заранее благодарю и поздравляю с Новым годом!!!

Это сообщение отредактировал(а) pinansonoyon - 30.12.2009, 14:56
PM MAIL   Вверх
COVD
Дата 30.12.2009, 00:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 17
Всего: 43



Цитата

возможна ситуация, при которой несколько потоков будут обрабатывать  редактировать один и тот же адрес одну и ту же запись


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

Это сообщение отредактировал(а) COVD - 30.12.2009, 00:25
PM MAIL   Вверх
Temdegon
Дата 30.12.2009, 05:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 429
Регистрация: 11.10.2008
Где: Minsk

Репутация: 7
Всего: 9



Логику блокировки я не понял вообще, как и не увидел в ней смысла. 
В главном потоке выберите все адреса из базы, разбейте полученный список на столько частей, сколько у вас потоков и передайте каждую часть в свой поток. Зачем вам здесь нужно в каждом потоке подключаться к базе и что-то там пытаться блокировать?
Может я не так понял, что вы хотите сделать? Можете подробнее описать задачу?
PM MAIL   Вверх
ivanovpv
Дата 30.12.2009, 08:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Варвар
**


Профиль
Группа: Участник
Сообщений: 639
Регистрация: 26.1.2005
Где: Москва

Репутация: 4
Всего: 28



Цитата(pinansonoyon @  29.12.2009,  23:51 Найти цитируемый пост)
Но ведь потоки ломанутся в базу и возможна ситуация, при которой несколько потоков будут
обрабатывать один и тот же адрес! 


А для чего существует механизм транзакций? Как нам говорит COVD, 
Цитата(COVD @  30.12.2009,  00:23 Найти цитируемый пост)
В базе данных по умолчанию должны быть внутренние блокировки, исключающие возможность одновременного редактирования данных разными клиентами или чтение данных, находящихся в процессе редактирования.


По сути это и есть механизм транзакций. Доступ к нему осуществляется на уровне Java посредством JTA. 

С точки зрения клиента блокировки предлагаемые автором бессмысленны, единственное чем надо озаботиться так это тем, чтобы не возник deadlock - когда 1 поток ожидает другого, а тот его.




--------------------
Aut viam inveniam aut faciam
PM MAIL Skype   Вверх
pinansonoyon
Дата 30.12.2009, 15:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 30.8.2009

Репутация: нет
Всего: нет



Привет, ребята! Спасибо, что откликнулись.

Значит, давайте сначало я вам расскажу что я знаю. Апдейт и инсерт команды в Постгри происходят так (во всяком случае, я так вычитал это). Сначало база получает  команду добавить или обновить кортеж. Потом она производит действия, в соответствии с командой, но не обновляет базу, а держит обновленный кортеж  во внутреннем представлении. Потом, если ничего не происходит (примерно так и написано, но не сказано- что значит ничего), обновленный кортеж заносится в базу данных. Если же происходит что-то (скорее всего, если его пытается обновить другой клиент), то обновляемый кортеж изменяется и только после этого добавляется в базу. Таким образом можно сделать вывод, что если кортеж находится еще во внутреннем представлении, то его могут изменять поочередно 2 или более клиентов. К чему это может привести? А это может привести к ситуации, при которой первичные изменения в кортеже даже не будут внесены в базу и никто вообще не узнает, что они были. Теоретически это возможно. Не доверять информации американской книжки у меня причин нет.

На этот случай у Постгри есть мощный механизм блокировок. Там много разных видов этой блокировки, но мой такой: 

Код

LOCK TABLE <table name> IN SHARE MODE;


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

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

2Temdegon
>>В главном потоке выберите все адреса из базы, разбейте полученный список на столько частей, сколько у вас потоков и передайте каждую часть в свой поток.

Хорошо, а как конкретно это сделать? как именно передать каждому потоку свой адрес?

Это сообщение отредактировал(а) pinansonoyon - 30.12.2009, 15:32
PM MAIL   Вверх
ivanovpv
Дата 30.12.2009, 17:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Варвар
**


Профиль
Группа: Участник
Сообщений: 639
Регистрация: 26.1.2005
Где: Москва

Репутация: 4
Всего: 28



Цитата(pinansonoyon @  30.12.2009,  15:23 Найти цитируемый пост)
Обмозговав все это, я сделал вывод, что надо блокировать кортежи или как-то их помечать. Иначе потоки будут их обрабатывать вместе.


В упор не понимаю, почему автор пытается изобрести велосипед. Есть же стандартные механизмы. Например в каждом потоке надо делать что-то такое:

Код

Connection con; //коннект с БД
try
{
    con.setAutoCommit(false); //включаем механизм ручных транзакций
    con.setTransactionIsolationLevel(TRANSACTION_SERIALIZABLE); //включаем самый крутой (но и медленный) уровень транзакций - по сути используем внутренние блокировки БД
    //делаем свои делишки
    ...
    con.commit(); //если все хорошо, проводим подтверждение
}
catch(SQLException ex)
{
    con.rollback(); // при ошибке откатываемся на исходную
}


При этом гарантируется, что следы работы другого потока не будут мешать данному потоку.


--------------------
Aut viam inveniam aut faciam
PM MAIL Skype   Вверх
Temdegon
Дата 30.12.2009, 19:57 (ссылка) |  (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 429
Регистрация: 11.10.2008
Где: Minsk

Репутация: 7
Всего: 9



Цитата

Хорошо, а как конкретно это сделать? как именно передать каждому потоку свой адрес?

Ну мало ли как. А как вы бы поступили, если нужно двум экземплярам одного класса передать два разных параметра? Можно в конструкторе, можно сеттером, можно еще что-нить придумать) 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0488 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.