Обучение и трудоустройство Java-программистов от Junior до Senior

Как работает synchronized

 
 
 
Сообщения:87
Вот задача:
Quote:
2. Реализовать многопоточное приложение, производящее те-
стирование целых чисел типа long на простоту в заданном диапазоне.
Для этого создается фиксированное количество потоков равное 10-
15% от количества элементов, каждый из которых случайным об-
разом обращается к непроверенному элементу последовательности,
проверенные элементы удаляются из списка.

В основном методе создаётся ArrayList из проверяемых чисел, создаётся массив потоков для проверки членов этого ArrayList-а на простоту.
package zad_028_1;

import java.util.ArrayList;
import java.util.List;

public class Main {

	public static void main(String[] args) {
		long nach,konec;
		nach=10;
		konec=30;
	List myList=new ArrayList();
		for (long i=nach;i<=konec;i++) {
			myList.add(i);
		}
		int kolvo=myList.size()/10+1;
		MyThread[] mythread=new MyThread[kolvo];
		for (int i=0;i<mythread.length;i++) {
			mythread[i]=new MyThread(myList,i); 
			mythread[i].start();
		}
	
	}

}

Здесь поток получает ссылку на аррэйлист, берет из него элемент, проверяет его на простоту и удаляет элемент из аррэйлиста.
public class MyThread extends Thread{
	List myList;
	int nomer;
	MyThread(List myList, int nomer) {
		this.myList=myList; 
		this.nomer=nomer;
	}
	
	public void run() {
		try {
			prostota(myList,nomer);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized  void prostota(List myList,int nomer) throws InterruptedException {
		while (myList.size()!=0) {
		int dlina = myList.size();
		int index = (int)(Math.round(Math.random()*dlina));
		if (index==dlina) index=dlina-1;
		long prov=(long)myList.get(index);
		myList.remove(index);
		boolean result;
		result=true;
			for (long k=2; k<=(long)Math.sqrt(prov);k++) {
				if (prov%k==0) {
				result=false;
				break;
			}
		
		}
		
		if (result) System.out.println("Я, поток №"+nomer+"заявляю, что "+prov+" это число простое.");
	}
	}
}


Метод, где удаляется элемент из аррэйлиста, сделал с оператором synchronized. И тем не менее сыпется ошибка:
Quote:
Exception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 20, Size: 18
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at zad_028_1.MyThread.prostota(MyThread.java:26)
at zad_028_1.MyThread.run(MyThread.java:15)
Я, поток №0заявляю, что 23 это число простое.
Я, поток №0заявляю, что 29 это число простое.
Я, поток №2заявляю, что 19 это число простое.
Я, поток №2заявляю, что 13 это число простое.
Я, поток №2заявляю, что 11 это число простое.
Я, поток №2заявляю, что 17 это число простое.

Значит, несколько потоков одновременно умудряются модифицировать аррейлист. Но разве не должен оператор synchronized препятствовать этому?
 
 
Сообщения:2415
Вы синхронизируетесь на разных объектах. synchronized на не статическом методе, захватывает монитор объекта на котором вызван метод.
 
 
Сообщения:514
vallball:
Значит, несколько потоков одновременно умудряются модифицировать аррейлист. Но разве не должен оператор synchronized препятствовать этому?
Неа, не должен. synchronized препятствует ОДНОВРЕМЕННОМУ изменению объекта несколькими потоками. Если правильно его использовать, конечно.
Например, в данном случае самое простое объявить
synchronized(myList){myList.remove(index);}


ЗЫ Все равно не взлетит, он из-за удаления крашится.

Я только учусь
 
 
Сообщения:87
Tachkin, из-за удаления элемента из списка крашится монитор?
Как же тогда быть?
 
 
Сообщения:514
vallball:
Tachkin, из-за удаления элемента из списка крашится монитор?

А при чем тут монитор? Падает по индексу коллекции.
Вот смотри: У тебя есть 10 яблок в ряд (коллекция), и 10 детей. Ты по очереди говоришь взять каждому ребенку по яблоку, но не "иди, возьми яблоко", а "тебе яблоко номер 1", "...2", "...3",..... Ребенок приходит и начинает считать яблоки.... Так вот уже шестой ребенок прибежит к тебе и скажет, что яблок только пять, а ты велел ему забрать шестое.
ЗЫ А захват монитора гарантирует, что стоять у стола и считать/забирать яблоки может только один ребенок.

Я только учусь
 
 
Сообщения:87
Tachkin:
vallball:
Tachkin, из-за удаления элемента из списка крашится монитор?

А при чем тут монитор? Падает по индексу коллекции.
Вот смотри: У тебя есть 10 яблок в ряд (коллекция), и 10 детей. Ты по очереди говоришь взять каждому ребенку по яблоку, но не "иди, возьми яблоко", а "тебе яблоко номер 1", "...2", "...3",..... Ребенок приходит и начинает считать яблоки.... Так вот уже шестой ребенок прибежит к тебе и скажет, что яблок только пять, а ты велел ему забрать шестое.
ЗЫ А захват монитора гарантирует, что стоять у стола и считать/забирать яблоки может только один ребенок.

Скорее тут логика такая: есть 30 яблок и три ребёнка. Каждый подходит к яблокам, берет цепочку из 10 штук и ищет, допустим, красные. Сваливает их в одну кучу, а зелёные в другую. При этом один ребенок, заняв место у стола, должен не подпускать в это время остальных.
 
 
Сообщения:458
мужик, ты тупишь.
там стоит, что каждый поток забирает себе одно значение, удаляя его из списка, потом проверяет и идёт за следующим.
 
 
Сообщения:87
Да, уже туплю.
Получается, что потоки делят между собой общий список. Как же сделать так, чтобы его не изменяли как попало, а по очереди?
 
 
Сообщения:2415
vallball:
При этом один ребенок, заняв место у стола, должен не подпускать в это время остальных.

А смотрите, что у Вас получилось. Каждый ребенок (инстанс класса MyThread) стал у стола, и действительно не пускает(через synchronized) к столу других детей, но проблема в том, что каждый стал у своего собственного стола, потому как synchronized применяется к совершенно разным объектам, а вам нужно выбрать единственную точку синхронизации.
 
 
Сообщения:2415
Поскольку Вы шарите между всеми потоками список, то он вполне сгодится за общую точку синхронизации:
public void prostota(List myList,int nomer) throws InterruptedException {
            synchronized(myList) {
                while (myList.size()!=0) {
                    int dlina = myList.size();
                    int index = (int)(Math.round(Math.random()*dlina));
                    if (index==dlina) index=dlina-1;
                    long prov=(long)myList.get(index);
                    myList.remove(index);
                    boolean result;
                    result=true;
                    for (long k=2; k<=(long)Math.sqrt(prov);k++) {
                        if (prov%k==0) {
                            result=false;
                            break;
                        }

                    }

                    if (result) System.out.println("Я, поток №"+nomer+"заявляю, что "+prov+" это число простое.");
                }
            }
        }

Ествественно это не то, что нужно сделать согласно зданию описанному в первом посте, так как теряется вся парелльность, но думаю вы уловили основную суть и более гранулярные локи напишите сами.
Изменен:04 окт 2017 06:56
 
 
Сообщения:87
А когда synchronized стоит перед методом prostota() монитор к чему привязывается? К потоку? Не ко всему, что лежит внутри этого метода, к чему он обращается? Думал, в том числе и к этому аррэйлисту.
За ответы всем спасибо.
 
 
Сообщения:2415
Я же Вам в самом первом ответе в этой теме писал, что монитор захватывается в этом случае на this, вот что у вас this в методе prostota то и лочится.
Изменен:04 окт 2017 07:32
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет