Проблема синхронизации общего объекта потока Java - PullRequest
3 голосов
/ 16 июля 2009

У меня проблемы с тем, что Synchronized ведет себя не так, как я ожидал, я попытался также использовать ключевое слово volatile:

Общий объект:


public class ThreadValue {
private String caller;
private String value;
public ThreadValue( String caller, String value ) {
    this.value = value;
    this.caller = caller;
}

public synchronized String getValue() {
    return this.caller + "     "  + this.value;
}
public synchronized void setValue( String caller, String value ) {
    this.caller = caller;
    this.value = value;
}
}

Тема 1:


class CongoThread implements Runnable {
    private ThreadValue v;
    public CongoThread(ThreadValue v) {
    this.v = v;

    }
    public void run() {
    for (int i = 0; i  10; i++) {
    v.setValue( "congo", "cool" );
    v.getValue();
    }
    }
}

Тема 2:


class LibyaThread implements Runnable {
    private ThreadValue v;
    public LibyaThread(ThreadValue v) {
    this.v = v;

    }
    public void run() {
    for (int i = 0; i  10; i++) {
       v.setValue( "libya", "awesome" );
       System.out.println("In Libya Thread " + v.getValue() );

    }
    }
}

Класс вызова:


class TwoThreadsTest {
    public static void main (String args[]) {

    ThreadValue v = new ThreadValue("", "");
        Thread congo = new Thread( new CongoThread( v ) );
        Thread libya = new Thread( new LibyaThread( v ) );

    libya.start();
        congo.start();

    }
}

Иногда я получаю "В Ливии Нить круто, Конго" что никогда не должно случиться. Я ожидаю только: "В Ливии Нить Ливия потрясающая" "In Congo Thread congo cool"

Я не ожидаю, что они будут смешанными.

Ответы [ 3 ]

4 голосов
/ 16 июля 2009

Почему бы им не быть смешанными? Хотя каждый отдельный вызов синхронизирован, ничто не мешает одному потоку вызвать v.setValue, затем поток other , вызывающий setValue, затем первый поток, вызывающий getValue (). Я верю, что так и происходит. Вы можете избежать этого, используя:

public void run() {
    for (int i = 0; i  10; i++) {
       synchronized (v) {
           v.setValue( "libya", "awesome" );
           System.out.println("In Libya Thread " + v.getValue() );
       }
    }
}

Таким образом, на каждой итерации он проверяет, вызывает ли он setValue и getValue, а другой поток не вызывает setValue.

Это не идеальный дизайн, по общему признанию - но я предполагаю, что эта демонстрация больше понимает синхронизацию, чем что-либо еще:)

2 голосов
/ 16 июля 2009

Что происходит, это следующее:

  1. Поток 1 устанавливает значение
  2. Thread 2 устанавливает значение
  3. Поток 1 считывает значение, установленное потоком 2.

Чтобы это исправить, вам нужно иметь 1 объект блокировки, который защищает вызов функций get / set для обоих потоков. Лучший способ сделать это - создать дополнительный синхронизированный метод, который выполняет как set, так и get. Однако иногда это не желательно. В этом случае предоставьте обоим потокам объект блокировки. Который является простым объектом. Который они затем используют в синхронизированном блоке.

Реализация каждого потока должна выглядеть следующим образом, обратите внимание, что они должны иметь точно такой же объект!

Object lockObject = new Object();
Thread t1 = new CongroThread(v, lockObject);
Thread t2 = new LibyaThread(v, lockObject);

...

class CongoThread implements Runnable {
    private ThreadValue v;
    private Object lockObject;

    public CongoThread(ThreadValue v, Object lockObject) {
    this.v = v;
    this.lockObject = lockObject,
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized(lockObject)
            {
                v.setValue( "libya", "awesome" );
                System.out.println("In Libya Thread " + v.getValue() );
            }
        }
    }
}
0 голосов
/ 16 июля 2009

Проблема в том, что v.getValue () для потока в Ливии вызывается сразу после того, как поток в Конго вызвал v.setValue (), что приводит к смешению.

Решение состоит в том, чтобы иметь блок потока ОБА и получить и установить значения, в противном случае у вас по-прежнему будет эта проблема. Вам придется вызывать метод получения внутри метода установки или использовать ожидание / уведомление, чтобы другой поток ожидал, пока один поток не установит и не получит значение.

...