Happens before java пример
Вот четыре основных правила «happens-before» в Java: Правило захвата монитора (Monitor Lock Rule): Если поток A захватывает монитор объекта X, а затем выполняет операцию, а поток B должен захватить тот же монитор X, чтобы увидеть результаты изменений, выполненных потоком A. Другими словами, все операции, выполненные потоком A до освобождения монитора X, будут видны потоку B после захвата того же монитора X.
// Поток A synchronized (lock) < sharedVariable = 10; >// Поток B synchronized (lock) < int value = sharedVariable; // Гарантируется, что значение 10 будет видно потоку B >
Даже на 64-битных платформах, использование volatile с 64-битными переменными, такими как long и double, может быть недостаточным для обеспечения атомарности сложных операций. Например, если два потока пытаются одновременно выполнить операцию инкремента на volatile long переменной, может возникнуть состояние гонки, потому что инкремент состоит из нескольких операций чтения, модификации и записи. В таких случаях для обеспечения атомарности операций или синхронизации между потоками следует использовать средства синхронизации, такие как synchronized блоки или классы из пакета java.util.concurrent.atomic, которые предоставляют атомарные операции над переменными, включая 64-битные переменные.
volatile в априори не может создавать атомарное представление переменной, он лишь отменяет ее кэширование, что косвенно делает ее атомарной, но volatile != 100% атомарность
Правило 4 «Запись в volatile переменную happens-before чтение из той же переменной» Само собой это не происходит, мы сами это регулируем. Если мы запустим чтение/запись с разных потоков, какой поток и когда прочитает переменную c volatile зависит от самих потоков, и их конкуренции. Сдесь вывод будет разный, но в основном по первому потоку который был запущен.
public class Main < public volatile static String message = "No changes"; public static void main(String[] args) throws InterruptedException < new FreeThread().start(); new MyThread().start(); >public static class MyThread extends Thread < @Override public void run() < message = "Message was changed"; >> public static class FreeThread extends Thread < @Override public void run() < System.out.println(message); >> >
public class Solution < public static volatile int proposal = 0; public static void main(String[] args) throws InterruptedException < Thread mt1 = new Mt1(); Thread mt2 = new Mt2(); mt1.start(); mt2.start(); Thread.sleep(100); System.out.println(proposal + " " +Thread.currentThread().getName()); >public static class Mt1 extends Thread < @Override public void run() < proposal = 1; try < Thread.sleep(100); >catch (InterruptedException e) < throw new RuntimeException(e); >System.out.println(proposal + " " +Thread.currentThread().getName()); > > public static class Mt2 extends Thread < @Override public void run() < proposal = 2; try < Thread.sleep(100); >catch (InterruptedException e) < throw new RuntimeException(e); >System.out.println(proposal + " " +Thread.currentThread().getName()); > >
А в этом же коде без слипов выводит значения, соответствующие установленным в каждом треде, и никто не ждет никаких записей от других потоков. Выходит, что последний пункт вот вообще не работает с точки зрения синхронизации, потому что результат зависит от скорости выполнения тредов, и в зависимости от нее результаты будут совершенно разными. Это уже не говоря о планировщике. Да, можно починить при помощи join, но в таком случае получается поделка на тему синхронизации и не более. Бред какой-то..