2015-08-24 2 views
2

У меня возникла проблема, когда мне приходится иметь дело со многими потоками, которые пытаются написать список строк в базе данных. Чтобы все было ясно, рассмотрим 3 потока, которые обращаются к объекту соединения с БД. Все потоки будут иметь String. Lets sat Thread A имеет строку под названием «First», Thread B имеет «Second», Thread C имеет «First». Предположим, что ниже - критическая часть кода, которую я хочу защитить, вкратце объясню.Использование семафора для размещения определенных потоков в очереди

writeToDataBase(String stringFromEachThread); 

Этот метод может выполняться потоками A и Thread B одновременно, поскольку в них есть разные значения строк. Я должен защитить вышеуказанный код, пока Thread A выполняется из Thread C, потому что Thread C также имеет такое же строковое значение, как Thread A. Мне нужно сделать Thread C до тех пор, пока поток A не завершит выполнение. Таким образом, я наткнулся на java.util.Concurrent.Semaphore класс, который помещает потоки в очередь. Но в моем случае только Thread C должен быть помещен в очередь не в Thread B. Поскольку Thread B имеет уникальное строковое значение, поэтому его можно выполнить параллельно с потоком A. Семафор блокирует поток C во время выполнения потока A, но он блокирует поток B также. Bowow - это фрагмент кода, в котором я использую семафор для достижения этого, который до сих пор не помог. Я занимаюсь этой проблемой уже более недели. Любые предложения по его преодолению глубоко признательны.

Semaphore sm = new Semaphore(1, true); 

... ...

public void lock(String str) throws InterruptedException { 
     if(!strAlreadExists()){ 
      return; 
     }else{ 
      sm.acquire(); 
     } 
} 
+0

Я бы предпочел использовать [synchronized] (https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html), прежде чем решил, что мне абсолютно нужны семафоры. Вот хороший учебник по «параллелизму», параметры, которые у вас есть, и компромиссы между этими параметрами: http://www.vogella.com/tutorials/JavaConcurrency/article.html – paulsm4

+0

И сколько строк это? Кроме того, даже если вы выполняете их одновременно, база данных будет сериализовать обращения. Так зачем беспокоиться? – fge

+0

* «База данных будет сериализовать доступ» * - не всегда, это будет зависеть от режима изоляции. –

ответ

1

Возможно, что у вас есть отдельные замки для отдельных строк. Ваш фрагмент кода, по-видимому, указывает на то, что вы считаете, что можете использовать один замок (будь то семафор или другой механизм блокировки), но каким-то образом он не делится между несвязанными потоками. И это не сработает.

Одним из способов достижения отдельной блокировки для отдельных строк является наличие карты из строки для блокировки. Вот иллюстрация с простым замком, а не семафор:

Объявите частное поле карты:

private final ConcurrentMap<String, Object> lockMap = new ConcurrentMap<>(); 

Создать метод для извлечения конкретной блокировки для данной строки:

private Object getLock(String str) { 

    Object candidateLock = new Object(); 
    Object returnedLock = lockMap.putIfAbsent(str, candidateLock); 
    return returnedLock == null ? candidateLock : returnedLock; 

} 

Для любой строки это сначала создаст блокировку кандидата и попытается поместить ее на параллельную карту.

Когда какой-либо поток пытается помещать кандидата на карту, на карте будет либо уже зафиксирована эта строка, либо блокировка-кандидат станет новой блокировкой для этой строки.Использование putIfAbsent(...) гарантирует, что все потоки, входящие в одну строку, получат один и тот же объект блокировки. Если на карте был зафиксирован замок, кандидат будет выброшен для сбора мусора.

Теперь, чтобы получить доступ к критичного кода, вы можете сделать:

Object lock = getLock(str); 

synchronized (lock) { 
    writeToDataBase(str); 
} 

Если вы настаиваете на использовании семафоров, вы можете использовать new Semaphore(...) вместо new Object() и использовать acquire вместо синхронизации. Но дело в том, что вы получаете отдельный замок/семафор для каждой отдельной строки.

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

+0

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

+0

@Parasuraman Нет, это совершенно другая ситуация. Если это то, что вам нужно, вы должны изменить свой вопрос, чтобы отразить свою реальную проблему, а не упрощенную проблему, которая не является реальной. Я предлагаю также показать, что находится внутри 'writeToDatabase', который требует защиты, в точности. – RealSkeptic

0

звучит как Striped Executor Service будет хорошо подходят.

Этот волшебный пул потоков будет гарантировать, что все Runnables с тем же stripeClass будут выполнены в порядке их представления, но StripedRunners с различными stripedClasses все еще может выполнить самостоятельно. Он хотел использовать относительно небольшой пул потоков для обслуживания большого количества клиентов Java NIO, но таким образом, чтобы исполняемые файлы все равно выполнялись в порядке.

Вы можете использовать String как ключ класса так каждая база данных записи, используя строку Asme сохраняет ИИТ порядок, а все остальные могут работать параллельно.

Смежные вопросы