#java #multithreading #class
Вопрос:
У меня есть два класса, которые представляют поток в java, Реализующий управляемый интерфейс, И класс с именем, который содержит 2 целых числа в качестве переменных класса (x и y).
Я хочу выполнить 2 простых действия, одно из которых-добавить x и y некоторых случайных чисел, dx и dy соответственно. (Метод обновления)
А другое действие-вернуть разницу между x и y. (метод getDiff)
Класс данных:
public class Data{
private int x = 0;
private int y = 0;
public Data (int x, int y){
this.x = x;
this.y = y;
}
public int getDiff(){
return (Math.abs(x,-y));
}
public void update(int dx, int dy){
x = x dx;
y = y dy;
}
}
Один поток должен выполнить обновление(int dx, int dy) 10 раз, а другой поток должен выполнить getDiff 10 раз. Им нужно распечатать результат (разница или новые значения x и y после обновления) и спать 10 мс между ними
Я хочу, чтобы поток чередовался, поэтому вот что я сделал:
Метод запуска класса Thread_1:
@Override
public void run() {
int rnd_x = 0;
int rnd_y = 0;
for (int i = 0 ; i<10; i )
{
rnd_x = (int) (Math.random() * 10);
rnd_y = (int) (Math.random() * 10);
data_obj.update(rnd_x,rnd_y);
System.out.println("Thread 1: Randomized " rnd_x " For x and " rnd_y " For y, New x: " data_obj.getX() ", New y: " data_obj.getY());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Метод выполнения Thread_2:
@Override
public void run() {
for(int i = 0; i<10; i )
{
System.out.println("Thread 2: Difference number " i " Is: " data_obj.getDiff());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Main method:
public static void main(String[] args) {
Data data_1 = new Data(1,1);
Thread t1 = new Thread(new Thread_1(data_1));
Thread t2 = new Thread(new Thread_2(data_1));
t1.start();
t2.start();
}
Update and getdiff after my changes:
public synchronized int getDiff() {
if (!flag)
{
notify();
flag = true;
return (Math.abs(x - y));
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return (Math.abs(x - y));
}
public synchronized void update(int dx, int dy) {
if (flag)
{
x = x dx;
y = y dy;
notify();
flag = false;
}
else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Я попытался использовать флаг и синхронизацию, чтобы они чередовались, но это не работает.
Как я могу заставить их чередоваться и почему то, что я пытался сделать, не работает?
Пример желаемого результата:
Thread 2: Difference number 0 Is: 0
Thread 1: Randomized 0 For x and 5 For y, New x: 1, New y: 6
Thread 2: Difference number 1 Is: 5
Thread 1: Randomized 0 For x and 4 For y, New x: 1, New y: 10
Thread 2: Difference number 2 Is: 9
Thread 1: Randomized 9 For x and 0 For y, New x: 10, New y: 10
Thread 2: Difference number 3 Is: 0
Thread 1: Randomized 8 For x and 1 For y, New x: 18, New y: 11
Thread 2: Difference number 4 Is: 7
Thread 1: Randomized 3 For x and 7 For y, New x: 21, New y: 18
Thread 2: Difference number 5 Is: 3
Thread 1: Randomized 7 For x and 8 For y, New x: 28, New y: 26
Thread 2: Difference number 6 Is: 2
Thread 1: Randomized 8 For x and 6 For y, New x: 36, New y: 32
Thread 2: Difference number 7 Is: 4
Thread 1: Randomized 3 For x and 5 For y, New x: 39, New y: 37
Thread 2: Difference number 8 Is: 2
Thread 1: Randomized 9 For x and 4 For y, New x: 48, New y: 41
Thread 2: Difference number 9 Is: 7
Thread 1: Randomized 2 For x and 7 For y, New x: 50, New y: 48
Комментарии:
1. Вы говорите «это не работает», но не объясняете, какое поведение вы наблюдаете. Все, что кажется неправильным в вашем коде, — это то, что после
wait
возврата вы не проверяетеflag
снова. Ты хочешь больше, какwhile (flag) wait(); notify(); flag = false; <rest of function>
иwhile (!flag) wait(); notify(); flag = true; <rest of function>
.
Ответ №1:
Нет простого механизма, чтобы делать то, что вы хотите. Как правило, с потоками это бесплатно для всех-если машина хочет, чтобы thread1 выполнялся в течение 1000 циклов, прежде чем thread2 получит доступ, вы ничего не можете с этим поделать!
Поэтому, если вы действительно хотите, чтобы они ждали друг друга, вам придется прямо попросить об этом. В java для этого есть инструмент под названием a CyclicBarrier
. В принципе, вам нужно сделать что-то вроде этого:
//2 because we have 2 threads in this example
CyclicBarrier barrier = new CyclicBarrier(2);
Thread t1 = new Thread(new Thread_1(data_1, barrier));
Thread t2 = new Thread(new Thread_2(data_1, barrier));
Затем вам нужно поместить barrier.await()
вызовы в каждый поток. Вот так:
public void run() {
int rnd_x = 0;
int rnd_y = 0;
for (int i = 0 ; i<10; i )
{
rnd_x = (int) (Math.random() * 10);
rnd_y = (int) (Math.random() * 10);
data_obj.update(rnd_x,rnd_y);
System.out.println("Thread 1: Randomized " rnd_x " For x and " rnd_y " For y, New x: " data_obj.getX() ", New y: " data_obj.getY());
barrier.await(); //let thread2 know you've produced a number
barrier.await(); //Wait for thread2 to consume the number
}
}
public void run() {
for(int i = 0; i<10; i )
{
//Wait for Thread1 to produce a number
barrier.await();
System.out.println("Thread 2: Difference number " i " Is: " data_obj.getDiff());
barrier.await(); //Let thread1 know you've consumed the number
}
Это должно сработать, и это аккуратно, но делать это в реальном производственном коде-плохая идея. Главная проблема в том, что следовать ей сложно — помните тот урок, который у вас был, когда кто-то объяснил, почему goto
это была плохая идея? Ну, здесь у вас есть похожая ситуация, когда на ваш код влияет какой-то другой код где-то в другом месте. Хуже того — если один поток создаст исключение, другой поток навсегда застрянет у барьера.
К сожалению, для того, чтобы лучше посоветовать вам, как избежать синхронизации, нам нужно будет больше знать о том, что вы пытаетесь сделать в первую очередь. Однако вы, вероятно, хотите SynchronousQueue
.
Более подробная информация здесь:
Комментарии:
1. Очень классное решение, но мне нужно использовать wait / nofity или блокировки, чтобы решить эту проблему.
2. @DvirPeretz — отлично, я предполагаю, что это какой-то вопрос для домашнего задания ;). В этом случае поток 1 вызывает
object.notify();
после того, как он произвел — затем он вызываетobject.wait()
, чтобы дождаться потребления. Затем поток 2 вызываетobject.wait()
ожидание данныхobject.notify()
. Вам также нужно быть осторожным при запуске, чтобы Thread2 ждал, прежде чем thread1 сообщит об этом в первый раз.