Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Java [игнор отключен] [закрыт для гостей] / Многопоточная запись в буфер / 11 сообщений из 11, страница 1 из 1
11.01.2012, 12:46:49
    #37609064
malahov.sa
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
Помогите, пожалуйста, разобраться со следующей проблемой.
Хочу сделать скидывание сообщений в некий буфер, при достижении порогового значения буфер сохраняется на диск. Код:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
import java.util.*;

public class ResourceWriter {

    public Map<Integer, Exception> exceptions = new HashMap<Integer, Exception>();
    private Map<String, String> map = new HashMap<String, String>();
    volatile boolean copying=false;
    volatile int count=0;

    public void write(String s){
        if (map.size()<100){
            while (copying){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            map.put(UUID.randomUUID().toString(), s);
            return;
        }
        sendSomewhere();
    }

    private synchronized void sendSomewhere() {
        copying=true;
        if (map.size()<100){
            copying=false;
            return;
        }


        System.out.println("Before flush:"+map.size()+";"+ (++count));
        Map<String, String> map2 = null;
        try {
            map2 = new HashMap<String, String>(map);
        } catch (Exception e) {
            exceptions.put(count, e);
            return;
        }
        map.clear();
        System.out.println("After flush:"+map.size()+";"+count);
        copying=false;
//        отправляем этот map2 куда-нибудь, эмуляция задержки
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

public class Worker extends Thread{

    private ResourceWriter rw;

    public Worker(ResourceWriter rw) {
        super();
        this.rw = rw;
    }

    @Override
    public void run() {
        for (int i=0;i<100;i++){
            rw.write(UUID.randomUUID().toString());
        }
    }
}

public class Main {

    public static void main(String[] args) {
        ResourceWriter rw = new ResourceWriter();
        for (int i=0;i<100;i++){
            new Worker(rw).start();
        }
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (Integer key:rw.exceptions.keySet()){
            System.out.println(key+";"+rw.exceptions.get(key).toString());
        }
    }

}



В строке
map2 = new HashMap<String, String>(map);
регулярно возникает ConcurrentModificationException, т.е. эту строку одновременно выполняет несколько потоков.
При этом вывод в консоль имеет следующий вид:

Before flush:104;1
Before flush:111;2
After flush:0;2

Т.е. карта, содержащая 104 строки утеряна. Таким образом получается, что метод
Код: java
1.
private synchronized void sendSomewhere() 


выполняется в нескольких потоках? Естественно, помечать метод write() как synchronized, не хочется
Нормальный вывод имеет вид:
Before flush:125;3
After flush:0;3
Before flush:100;4
After flush:0;4
...
Рейтинг: 0 / 0
11.01.2012, 12:54:35
    #37609089
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
Взять готовый кэш не вариант? EhCache, например.
...
Рейтинг: 0 / 0
11.01.2012, 12:57:27
    #37609096
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
Ещё для общего развития рекомендую изучить вот этот package
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/package-summary.html
и разобратся с задачей читателей и писателей (ReadWriteLock)
...
Рейтинг: 0 / 0
11.01.2012, 13:21:18
    #37609161
Miha_S7
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
malahov.sa,

что касается ошибки, то она возникает при доступе к map. При создании map2 вы указываете базовую карту, доступ к которой не синхронизирован и возможен из write() и sendSomewhere()
...
Рейтинг: 0 / 0
11.01.2012, 13:29:50
    #37609190
malahov.sa
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
BlazkowiczВзять готовый кэш не вариант? EhCache, например.
Да собственно, он и взят уже) У меня не практический вопрос, а скорее, теоретический. Если честно, смотрел уже в этот пакет, особенно на ConcurrentLinkedQueue. Немного смущает в исходниках наличие misc.unsafe.
...
Рейтинг: 0 / 0
11.01.2012, 13:31:54
    #37609199
malahov.sa
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
Miha_S7,

почему не синхронизирован?
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
    volatile boolean copying=false;
    

    public void write(String s){
        if (map.size()<100){
            while (copying){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            map.put(UUID.randomUUID().toString(), s);
            return;
        }


пока идет копирование add не вызывается.
...
Рейтинг: 0 / 0
11.01.2012, 13:36:02
    #37609205
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
[quot malahov.sa]
while (copying) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
[/src]
Это худший вариант синхронизации, который только можно было придумать. Он помимо того что занимает поток так же как и synchronized, так ещё и жрет попусту CPU.
Если это просто неудачная попытка реализовать spinlock, то этого делать не нужно. spinlock уже реализован для synchronized.
...
Рейтинг: 0 / 0
11.01.2012, 13:37:52
    #37609214
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
malahov.saЕсли честно, смотрел уже в этот пакет, особенно на ConcurrentLinkedQueue. Немного смущает в исходниках наличие misc.unsafe.
Смущают в каком плане? Слово страшное?
...
Рейтинг: 0 / 0
11.01.2012, 13:42:24
    #37609224
Blazkowicz
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
malahov.saУ меня не практический вопрос, а скорее, теоретический.
Если вопрос теоретический, то я уже дал наиболее полезный совет
1-е разобратся в теоретической задаче читаталей и писателей. Материала даже по-русски валом.
2-е разобратся с вышеуказаным пакетом. Дабы не городить бесполезных Worker-ов и не изобретать Lock заново.
...
Рейтинг: 0 / 0
11.01.2012, 13:48:20
    #37609237
Miha_S7
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
malahov.sa,

представим ситуацию.
T1 прошёл по write дошёл до sendSomewhere();

вызывается sendSomewhere: получаем блокировку на this устанавливаем флаг copying

пока мы получали блокировку вылезает другой тред T2, который в строчке while (copying) видит, что флаг не установлен(T1 ещё не установил его, так как занят получением блокировки) и проделывает операции в картой. Дальше всё понятно.

Если вам пришла в голову идея сделать что-нибудь наподобие:
Код: java
1.
2.
3.
4.
private void sendSomewhere() {
    copying=true;
    synchronized {
...


то это плохая идея. Самый простой вариант - использовать ConcurrentHashMap для map и вместо
Код: java
1.
map.clear();


сделать
Код: java
1.
2.
3.
for(String i : map2.keySet()) {
    map.remove(i);
}
...
Рейтинг: 0 / 0
11.01.2012, 13:57:25
    #37609262
Miha_S7
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Многопоточная запись в буфер
и, конечно же, убрать флаг copying и всё, что с ним связано
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Многопоточная запись в буфер / 11 сообщений из 11, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]