Синхронизация

 
 
 
Сообщения:103
Всем привет. Начинаю изучение потоков. Чтобы проверить себя написал следующую простую программку

public class PTest {
    public static void main(String[] args) {
        BhThread bhThread1 = new BhThread();
        BhThread bhThread2 = new BhThread();
        BhThread bhThread3 = new BhThread();
    }
}

class Bh {
    synchronized public void meth() {
        try {
            for (int i = 1; i <= 10; i++) {
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
            System.out.println("Поток прерван");
        }
    }
}

class BhThread implements Runnable {
    static Bh bh = new Bh();
    Thread t;

    public BhThread() {
        t = new Thread(this);
        t.start();
    }

    @Override
    public void run() {
        bh.meth();
    }
}


Если в классе BhThread объект bh не объявить как static, то результат такой
i = 1
i = 1
i = 1
i = 2
i = 2
i = 2
i = 3
i = 3
i = 3
...


А если объявить как статический, то
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10
i = 1
i = 2
i = 3


Правильно ли я понял, что нужно объявить как статический данный объект, потому что все три потока использую один и тот же объект?
 
 
Сообщения:1403
  public BhThread() {
        t = new Thread(this);
        t.start();
    }

Очень плохо, нельзя работать с объектом пока он полностью не инициализировался

synchronized public void meth() {

Блокировка на уровне данного объекта, то есть метод нельзя будет вызвать из разных потоков для одного объекта

static Bh bh = new Bh();

Модификатор static говорит что объект принадлежит классу, а не объекту. И bh.meth(); обращаются к одному объекту и блокируют друг друга, без static у каждого потока будет свой объект и они могут выполнять код одновременно.
 
 
Сообщения:103
public BhThread() {
        t = new Thread(this);
        t.start();
    }


Вместо этого нужно в методе main создать поток тип так? Я правильно понимаю?

BhThread bh1 = new BhThread();
Thread potok1 = new Thread(bh1);
potok1.start();


И второе

synchronized public void meth() {


Как тут лучше сделать?

если я в классе BhThread напишу так будет правильнее?

public class PTest {
    public static void main(String[] args) {
        BhThread bhThread1 = new BhThread();
        BhThread bhThread2 = new BhThread();
        BhThread bhThread3 = new BhThread();
		
		Thread p1 = new Thread(bhThread1);
		Thread p2 = new Thread(bhThread2);
		Thread p3 = new Thread(bhThread3);
		
		p1.start();
		p2.start();
		p3.start();
    }
}
 
class Bh {
    public void meth() {
        try {
            for (int i = 1; i <= 10; i++) {
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
            System.out.println("Поток прерван");
        }
    }
}
 
class BhThread implements Runnable {
    static Bh bh = new Bh();
 
    public BhThread() {

    }
 
    @Override
    public void run() {
        synchronized (bh) {
			bh.meth();
		}
    }
}
 
 
Сообщения:1403
Quote:
Как тут лучше сделать?

А какая задача стоит?
 
 
Сообщения:103
Задачи нет конкретной. Я учусь. И хотелось бы узнать как лучше делать. сделать доступ к объекту синхронизированным или методы объявить как синхронизированные.
 
 
Сообщения:1403
Quote:
И хотелось бы узнать как лучше делать

Без задачи нельзя сказать как лучше. Если у вас стоит цель чтобы к операции имел доступ только 1 поток, то верен способ со static, в обратном случае второй способ. Этот вопрос звучит как "Как правильно готовить мясо?"
 
 
Сообщения:103
Это я уже понял)

А то что вы написали "Очень плохо, нельзя работать с объектом пока он полностью не инициализировался". Т.е. не желательно создавать поток в конструкторе класса?

И еще вы писали
Quote:

synchronized public void meth() {

Блокировка на уровне данного объекта, то есть метод нельзя будет вызвать из разных потоков для одного объекта


но в моем примере я создал 3 потока и из всех трех я обращался в этому методу. Почему его нельзя вызвать из разных потоков для одного объекта?
 
 
Сообщения:103
Представим ситуацию, что у нас многопотоковое приложение, написанное на Java. Есть некий класс, выполняющий какое-либо конкретное действие с данными, например, отправку их по почте, а сами данные подготавливаются в другом месте кода в другом потоке. Перед отправкой данных нам необходимо как-то связаться с потоком, подготавливающим данные, дабы поймать момент, когда они будут готовы.

Пример 3. Ожидание в цикле.


public class DataManager implements Runnable {
    private static final Object monitor = new Object();
    private static boolean ready = false;

    public void prepareData() {
        synchronized (monitor) {
            System.out.println("Data prepared");
            ready = true;
            monitor.notifyAll();
        }
    }

    public void sendData() {
        synchronized (monitor) {
            System.out.println("Waiting for data...");
            while (!ready) {
                try {
                    monitor.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // continue execution and sending data
            System.out.println("Sending data...");
        }
    }
}
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
public class DataManager implements Runnable {
    private static final Object monitor = new Object();
    private static boolean ready = false;
 
    public void prepareData() {
        synchronized (monitor) {
            System.out.println("Data prepared");
            ready = true;
            monitor.notifyAll();
        }
    }
 
    public void sendData() {
        synchronized (monitor) {
            System.out.println("Waiting for data...");
            while (!ready) {
                try {
                    monitor.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
 
            // continue execution and sending data
            System.out.println("Sending data...");
        }
    }
}


Можно ли этот пример рассматривать как основу, для понимания взаимодействия потоков? Или можете привести свой простой пример взаимодействия потоков с применением методов wait(), notify(), notifyAll(), желательно с комментариями. Спасибо.

P.S. Я в принципе понимаю что метод wait() приостанавливает выполнение потока пока другой поток не уведомит его вызовом метода notify() или notifyAll(). Но как пример организовать такой самому.
 
 
Сообщения:1403
Quote:
А то что вы написали "Очень плохо, нельзя работать с объектом пока он полностью не инициализировался". Т.е. не желательно создавать поток в конструкторе класса?

Нежелательно начинать работать с объектом до того как он полностью проинициализируется

Quote:
Почему его нельзя вызвать из разных потоков для одного объекта?

потому что synchronized как раз и создан для запрещения такого

Quote:
Можно ли этот пример рассматривать как основу, для понимания взаимодействия потоков?

Ну если не смотреть на запутанную логику, то можно. Ещё к переменной ready надо на всякий случай добавить volitile
 
 
Сообщения:103
loptop:
Ну если не смотреть на запутанную логику, то можно. Ещё к переменной ready надо на всякий случай добавить volitile


А можете привести простой пример с Не запутанной логикой чтобы стало понятнее когда и как примерять эти методы? Не поняв потоки не хочется дальше двигаться. Все-таки нужная вещь потоки. Буду благодарен, за простой и понятный пример
 
 
Сообщения:1403
Не знаю какой пример вам привести, тут есть всего пару моментов

monitor.wait(); - останавливает поток и ждет пока его не освободят
monitor.notify(); - освобождает один из потоков которые остановились для объекта monitor
monitor.notifyAll(); - освобождает все потоки которые остановились на объекте monitor

А вообще можно использовать специальные объекты, типа Lock
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет