Java Concurrency (2) — расскажите о том, что произойдет до того, как

Java задняя часть программист переводчик

введение

В предыдущей статье говорилось о модели памяти Java, в которой мы сказали, что JMM основан на принципе «происходит до».

Почему ты это сказал? Потому что во время выполнения Java-программы компилятор и процессор выполняют серию оптимизаций кода, который мы пишем, чтобы повысить эффективность выполнения программы. Это включает в себя «переупорядочивание» инструкций.

Изменение порядка приводит к тому, что наш код не выполняется в том порядке, в котором код был написан, поэтому наши результаты не путаются после выполнения программы, причина в том, что модель памяти Java следует принципу «происходит до». В соответствии с правилом «происходит до того, как» независимо от того, как будет переупорядочена программа, результат выполнения не изменится, поэтому мы не увидим искажения результата программы.

изменение порядка

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

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

После того, как переупорядочение происходит во время выполнения одного потока, мы не можем его воспринять, как показано в следующем коде:

int a = 1;  //步骤1
int b = 2;  //步骤2
int c = a + b; //步骤3 

Изменение порядка 1 и 2 не повлияет на результат выполнения программы.В некоторых случаях 1 и 2 могут быть изменены для оптимизации производительности. Изменение порядка 2 и 3 влияет на результат выполнения, поэтому компилятор и процессор не будут изменять порядок 2 и 3.

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

public class AAndB {

	int x = 0;
	int y = 0;
	int a = 0;
	int b = 0;
	
	public void awrite() {

		a = 1;
		x = b;
	}
	
	public void bwrite() {

		b = 1;
		y = a;
	}
}

public class AThread extends Thread{

	private AAndB aAndB;
	
	public AThread(AAndB aAndB) {
		
		this.aAndB = aAndB;
	}
	
	@Override
	public void run() {
		super.run();
		
		this.aAndB.awrite();
	}
}

public class BThread extends Thread{

	private AAndB aAndB;
	
	public BThread(AAndB aAndB) {
		
		this.aAndB = aAndB;
	}
	
	@Override
	public void run() {
		super.run();
		
		this.aAndB.bwrite();
	}
}

private static void testReSort() throws InterruptedException {

	AAndB aAndB = new AAndB();

	for (int i = 0; i < 10000; i++) {
		AThread aThread = new AThread(aAndB);
		BThread bThread = new BThread(aAndB);

		aThread.start();
		bThread.start();

		aThread.join();
		bThread.join();

		if (aAndB.x == 0 && aAndB.y == 0) {
			System.out.println("resort");
		}

		aAndB.x = aAndB.y = aAndB.a = aAndB.b = 0;

	}

	System.out.println("end");
}

Без переупорядочивания есть четыре возможности порядка выполнения программы:

Но программа будет печатать «resort» после многократного выполнения, что означает, что и поток A, и поток B переупорядочиваются.

определение «происходит до»

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

  1. Правило порядка выполнения программы: В отдельном потоке, в соответствии с последовательностью выполнения программного кода, операция, выполняемая первой (по времени), происходит раньше (по времени), а операция выполняется после.
  2. Управление правилами блокировки: операция разблокировки происходит до (последовательность во времени, то же самое ниже) операции блокировки той же блокировки.
  3. Правила для изменчивых переменных: Запись в изменчивую переменную, работающую за операцией «происходит до чтения» переменных.
  4. Правила запуска потока: Метод Start () объекта Thread Произойдет перед этим действием.
  5. Правила завершения потока: все операции потока происходят до того, как будет обнаружено завершение потока, и можно определить, что поток завершил выполнение с помощью конца метода Thread.join() и возвращаемого значения Thread. .является живым().
  6. Правила прерывания потока: вызов метода прерывания() потока происходит до того, как произойдет событие, когда код прерванного потока обнаружит прерывание.
  7. Правила финализации объекта: Инициализация объекта (окончание выполнения конструктора) происходит до запуска его метода finalize().
  8. Транзитивность: если операция А происходит до операции В, а операция В происходит до операции С, то можно сделать вывод, что А происходит до операции С.

«случается-прежде» определяет так много правил, которые можно суммировать в одном предложении: правила «события-прежде» гарантируют, что результаты выполнения однопоточных и правильно синхронизированных многопоточных операций не будут изменены.

Зачем правила последовательности программы обеспечения, многопоточное выполнение вышеупомянутого или появление переупорядочения его? Это потому, что только происходит - до правила - обеспечить, чтобы модель памяти Java сделала программист. В однопоточной резьбоке ему не волнует порядок выполнения программы, чтобы обеспечить, чтобы только один резьбовые результаты выполнения в рамках программы должны быть правильными, модель памяти Java позволяет компилятору и процессору в произошедших правилам Переупорядочение выполнения программы.

И с точки зрения программиста важно не то, действительно ли переупорядочены две операции, а то, изменился ли результат выполнения программы.

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

как-будто-серийная семантика

Объяснено в The Art of Java Concurrent Programming:

Как-будто-последовательный означает, что независимо от того, насколько переупорядочены (компилятор и процессор для улучшения параллелизма), результат выполнения (однопоточной) программы не может быть изменен. Компилятор, среда выполнения и процессор должны подчиняться семантике «как если бы — последовательная».

Популярное понимание этого предложения состоит в том, что семантика «как если бы» гарантирует, что результат выполнения однопоточной программы не изменится.

По сути, то же значение, что и у правила «происходит до»: правило «происходит до» гарантирует, что результат выполнения одного потока и правильно синхронизированной многопоточности не будет изменен. Все гарантируют результат выполнения, но не процесс выполнения.

Это тоже изюминка дизайна JMM: он не только обеспечивает удобство и корректность программирования для программистов, но и обеспечивает большую свободу оптимизации для компиляторов и процессоров.
Использованная литература: "Углубленное понимание модели памяти Java" "Глубокое понимание виртуальной машины Java" Искусство параллельного программирования на Java