LINUX.ORG.RU

Java инкапсуляция

 ,


1

3

Доброе утро,

Пробую кое-что на Java, довольно долго, и не выходит. Не могу разобраться, ситуация такая:

3 класса: main, first, second. First & second работают как потоки, у меня идея заставить один класс кое-как модифицировать переменную main'a, а second детектить изменения и писать что-то при изменении этой переменной в main классе.

Но не пойму инкапсуляции, где же мне создать инстанс и как, что бы все классы могли видеть его? Я создал функцию сеттер, но классы не видят инстанса из другого класса.

Спасибо.

wait()/notify() на ссылке на инстанс main

/thread

x0r ★★★★★ ()
Последнее исправление: x0r (всего исправлений: 1)

В джаве всё это через коллбеки делается. У main вызывается метод setWhatever, в котором идёт изменение переменной и оповещение всех «слушателей» о том, что переменная изменилась.
Ну а сам метод setWhatever можно сделать synchronized, или использовать atomic-и.

GblGbl ★★★★★ ()
Ответ на: комментарий от GblGbl

Вот, в этом-то и проблема. Нужно создавать инстанс класса, но где?

Вот пример моего main.

first First = new first(); //и прочий код для запуска потока second Second = new second(); main Main = new main();

Но написав внутри одного из классов,

Main.setValue(42);

Я гарантировано получу unknown variable.

maxylopes ()
Ответ на: комментарий от maxylopes

Если совсем по понятиям делать, то будет как-то так:

interface ValueChangeListener<T> {
    public void valueChanged(T newValue);
}

class Main {
    private Type var;
    private List<ValueChangeListener<Type>> listeners;

    public synchronized void setValue(Type newValue){
       var = newValue;
       for (ValueChangeListener<Type> l: listener){
           l.valueChanged(newValue);
       }
    }

    public void addListener(ValueChangeListener<Type> listener){
        listeners.add(listener);
    }
}

class B implements ValueChangeListener<Type> {...}
class C implements ValueChangeListener<Type> {...}
class D implements ValueChangeListener<Type> {...}

Main m = new Main();
B b = new B();
C c = new C();
D d = new D();

m.addListener(a);
m.addListener(b);
m.addListener(c);

И теперь, когда кто-то будет менять значение переменной, класс main будет дёргать всех своих listener-ов. Как-то так, короче.

GblGbl ★★★★★ ()
Ответ на: комментарий от GblGbl

Серьезно, это самый простой способ? ._. Суть понятна, но что-то на С++ реализация в раз 10 проще была.

maxylopes ()
Ответ на: комментарий от maxylopes

Ну тебе никто не запрещает передать экземплярам first и second ссылку на экземпляр main.

Begemoth ★★★★★ ()
Ответ на: комментарий от maxylopes

В Java экземпляры классов тебе доступны уже по ссылке. Никаких указателей. Т.е. m, a, b, c - уже ссылки.

anonymous ()
Ответ на: комментарий от maxylopes

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

Begemoth ★★★★★ ()
Ответ на: комментарий от maxylopes
import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
	public void firstMethod() {
		System.out.println("First");
	}
	
	public void secondMethod() {
		System.out.println("Second");
	}
}

class ManagedThread extends Thread {
	protected Main main;
	
	public ManagedThread(Main main) {
		this.main = main;
	}
}

class First extends ManagedThread {
	public First(Main main) {
		super(main);
	}
	
	public void run() {
		System.out.println("First::run()");
		synchronized (this.main) {
			System.out.println("Calling Main::firstMethod()");
			this.main.firstMethod();
			System.out.println("Calling Main::notify()");
			this.main.notify();
		}
	}
}

class Second extends ManagedThread {
	public Second(Main main) {
		super(main);
	}
	
	public void run() {
		System.out.println("Second::run()");
		synchronized (this.main) {
			try {
				System.out.println("Calling Main::wait()");
				this.main.wait();
			} catch (InterruptedException ex) {
				System.out.println(ex);
			}
			System.out.println("Calling Main::secondMethod()");
			this.main.secondMethod();
		}
	}
}

class Ideone
{
	public static void main (String[] args) throws java.lang.Exception
	{
		Main main = new Main();
		Second second = new Second(main);
		second.start();
		First first = new First(main);
		first.start();
		first.join();
		second.join();
	}
}
x0r ★★★★★ ()
Ответ на: комментарий от GblGbl

Ъ интирпрайз, тут лаба для студентоты.

x0r ★★★★★ ()
Ответ на: комментарий от x0r

1. импорты не нужны;

2. Метод ожидания wait() используется неправильно. Он должен проверяться вызываться циклически, пока какое-либо условие (например, условие модификации объекта) не поменяется;

3. Дизайн — жесть.

iZEN ★★★★★ ()
Последнее исправление: iZEN (всего исправлений: 1)

Вот что накропал совместно с авторами JLS

package izenfire.produceconsume;

/**
 * Объект-Значение
 */
class DataObject {

    private volatile int value;
    private boolean ready;

    public DataObject() {
    }

    public synchronized void put(int newvalue) {
        try {
            Thread.sleep(1000);//Имитация временных затрат на модификацию Значения
        } catch (InterruptedException iex) {
        }
        value = newvalue;
        ready = true;
        this.notifyAll();//сигнал всем заблокированным
    }

    public synchronized int take() throws InterruptedException {
        while (!ready) {//ожидание модификации, снимающей блокировку
            wait();
            System.out.printf(" готово=%b ", ready);
        }
        ready = false;//значение прочтено
        try {
            Thread.sleep(2500);//Имитация временных затрат на чтение Значения
        } catch (InterruptedException iex) {
        }
        return value;
    }
}

/**
 * Модификатор
 */
class Modifier extends Thread {

    public boolean done;
    private final int ID;
    private final DataObject modifiable;

    public Modifier(int ID, DataObject modifiable) {
        this.ID = ID;
        this.modifiable = modifiable;
    }

    @Override
    public void run() {
        int nv = 0;
        while (!done) {
            nv++;
            modifiable.put(nv); //модифицировать Значение
            System.out.printf("Modifier%d присвоил значение=%d; ", ID, nv);
        }
    }
}

/**
 * Наблюдатель
 */
class Observer extends Thread {

    public boolean done;
    private final int ID;
    private final DataObject observable;

    public Observer(int ID, DataObject observable) {
        this.ID = ID;
        this.observable = observable;
    }

    @Override
    public void run() {
        int value = 0;
        while (!done) {
            try {
                value = observable.take(); //получить модифицированное Значение
            } catch (InterruptedException ex) {
            }
            System.out.printf("Observer%d увидел значение=%d.\n", ID, value);
        }
    }
}

public class ModifyObserveMain {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Старт симуляции.");
        DataObject value = new DataObject();
        Modifier modifier1 = new Modifier(1, value);
        //Modifier modifier2 = new Modifier(2, value);
        modifier1.start();
        //modifier2.start();
        Observer observer1 = new Observer(1, value);
        //Observer observer2 = new Observer(2, value);
        observer1.start();
        //observer2.start();
        //Система работает 30 секунд
        Thread.sleep(30000);
        modifier1.done = true;
        //modifier2.done = true;
        observer1.done = true;
        //observer2.done = true;
        synchronized (value) {
            value.notifyAll(); //Будим всех заблокированных
        }
        System.out.println("Стоп симуляции.");
    }
}

При этом объект класса DataObject имеет атомарную/квантовую/ природу своего содержимого: в нём можно хранить значение (в данном случае value типа int) до «взятия» значения кем-то.

iZEN ★★★★★ ()
Последнее исправление: iZEN (всего исправлений: 2)

Воах, спасибо огромное, что заставили меня навалить от страха напугав меня размерами кода, и с адреналином в крови сделать же таки простое решение.

Как я реализовал:

1. Статическая переменная. 2. Объявление и присвоение эгземпляра мэйн класса, и в первом и во втором классе (сборщик мусора все приберет). 3. Так как переменная статическая, методы внутри созданных эгземпляров меняют не свою локальную переменную, а ту самую статическую.

Суть программы была просто в том что бы проверить, насколько хорошо синхронизированы потоки. И все что я делаю, первым потоком пишу миллисекунды, вторым сравниваю. Получается в ~40% сихронизация с точностью в миллисекунду, в остальных ~60% разрыв 1-4 миллисекунды (можно ли микросекунды проверить? :))

Спасибо всем за предложенные решения.

maxylopes ()

Джошуа Блох не согласен...

В соответствии рекомендациями известного жабавода предлагаю следующий более корректный код:

package izenfire.produceconsume;

/**
 * Объект-Значение
 */
class DataObject {

    private volatile int value;
    private boolean ready;

    public DataObject() {
    }

    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);//Имитация временных затрат
        } catch (InterruptedException iex) {
        }
    }

    public synchronized void put(int newvalue) {
        sleep(1000);//Имитация временных затрат на модификацию Значения
        value = newvalue;
        ready = true;
        this.notifyAll();//сигнал всем заблокированным
    }

    public synchronized int take() throws InterruptedException {
        while (!ready) {//ожидание модификации, снимающей блокировку
            wait();
            System.out.printf("готово=%b; ", ready);
        }
        ready = false;//значение прочтено
        sleep(2500);//Имитация временных затрат на чтение Значения
        return value;
    }
}

/** Управляемая нить */
class ManagedThread extends Thread {

    protected final int ID;
    private boolean stopRequested = false;

    protected ManagedThread(int ID) {
        this.ID = ID;
    }

    public final synchronized void requestStop() {
        stopRequested = true;
    }

    public final synchronized boolean stopRequested() {
        return stopRequested;
    }
}

/**
 * Модификатор
 */
class Modifier extends ManagedThread {

    private final DataObject modifiable;

    public Modifier(int ID, DataObject modifiable) {
        super(ID);
        this.modifiable = modifiable;
    }

    @Override
    public void run() {
        boolean done = false;
        int nv = 0;
        while (!stopRequested() && !done) {
            nv = (int) (100 * Math.random());
            modifiable.put(nv); //модифицировать Значение
            System.out.printf("Modifier%d присвоил значение=%d; ", super.ID, nv);
        }
    }
}

/**
 * Наблюдатель
 */
class Observer extends ManagedThread {

    private final DataObject observable;

    public Observer(int ID, DataObject observable) {
        super(ID);
        this.observable = observable;
    }

    @Override
    public void run() {
        boolean done = false;
        int value = 0;
        while (!stopRequested() && !done) {
            try {
                value = observable.take(); //получить модифицированное Значение
            } catch (InterruptedException ex) {
            }
            System.out.printf("Observer%d увидел значение=%d.\n", super.ID, value);
        }
    }
}

/** Use Case */
public class ModifyObserveMain {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Старт симуляции.");
        DataObject value = new DataObject();
        Modifier modifier1 = new Modifier(1, value);
        //Modifier modifier2 = new Modifier(2, value);
        //Modifier modifier3 = new Modifier(3, value);
        modifier1.start();
        //modifier2.start();
        //modifier3.start();
        Observer observer1 = new Observer(1, value);
        //Observer observer2 = new Observer(2, value);
        observer1.start();
        //observer2.start();
        /* Система работает 30 секунд */
        Thread.sleep(30000);
        modifier1.requestStop();//Быстро и чисто останавливаем модификатор
        //modifier2.requestStop();
        //modifier3.requestStop();
        observer1.requestStop();//Быстро и чисто останавливаем наблюдатель
        //observer2.requestStop();
        synchronized (value) {
            value.notifyAll(); //Будим всех заблокированных
        }
        System.out.println("Стоп симуляции.");
    }
}

iZEN ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.