Из того, что я читал, порядок, в котором потоки получают доступ к синхронизированному методу, не указан и, следовательно, зависит от реализации, но похоже, что в Oracle Java 8 (1.8.0_101-b13, 64-разрядная версия на Ubuntu) наиболее обычно отдается предпочтение недавно запущенному потоку.
Например, когда я запускаю следующий класс:
class C
{
synchronized void go()
{
try
{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName());
System.out.flush();
}
catch (Exception e)
{
System.out.println(e);
}
}
public static void main(String[] args)
{
C c = new C();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(((Runnable) c::go));
t.setName(Integer.toString(i));
t.start();
}
}
}
Обычно я получаю такие результаты:
0 98 99 97 96 95 94 91 93 ... 4 3 2 1
Может ли кто-нибудь подтвердить, что Oracle JVM имеет тенденцию делать это? А есть ли какая-то особая причина?
Что вы имеете в виду под «последним запущенным потоком»? Каждый из потоков в вашем примере вызывает go() и блокирует мьютекс ровно один раз. Вы имеете в виду последнюю ветку начал?
Ваш пример не показывает хорошее использование synchronized, потому что он выполняет ввод-вывод, удерживая блокировку. Вам следует избегать держать блокировку заблокированной дольше, чем требуется вашей программе для присвоения нескольких переменных. Если вы будете следовать этому правилу, то блокировка редко будет вызывать конкуренцию, и вам будет все равно, является она «справедливой» или нет.
@markspace, что значит "используя процессы ...?" В нескольких известных мне операционных системах поток - это исполняемый контекст (поток - это то, что планирует планировщик), а процесс - это совокупность ресурсов (открытые файлы, виртуальное адресное пространство и т. д.), И каждый поток "принадлежит "один процесс. В операционной системе Linux, в которой с самого начала не было потоков, идея «потока» лишь постепенно отделялась от идеи «процесса»; но есть и другие операционные системы, в которых с самого начала потоки были отдельными объектами.
@jameslarge Я никогда не слышал о процессе, называемом сбором ресурсов. Процесс - это исполняемый контекст, подобный потоку. То, что он содержит открытые файлы и т. д., Просто необходимая реализация обслуживания ОС. Разница между потоками и процессами заключается в том, что потоки совместно используют пространство кучи, а процессы - нет.
@markspace, мы с тобой разговариваем на разных уровнях. Мне кажется, вы говорите о выборе, который сделает разработчик программного обеспечения: структурировать ли какое-либо приложение как совокупность взаимодействующих процессов или как совокупность взаимодействующих потоков в рамках одного процесса. Я говорю о структурах данных в ядре операционной системы. В операционных системах некоторый, которые я использовал (и в некоторых, над кодом которых я работал), объект, называемый «процесс», никоим образом не похож на объект, называемый «поток».
@jameslarge Я говорю о дизайне ОС и ее ядра. Некоторые системы поддерживают виртуальную память, некоторые - нет. Некоторые поддерживают отслеживание файлов для каждого процесса, некоторые - нет. Дизайн потоков, побуждение к созданию чего-то другого, отличного от процесса, было наблюдением, что было бы преимуществом, если бы процессы могли совместно использовать динамическую память. Эти более легкие процессы были названы потоками. Прошло некоторое время с тех пор, как я копался во внутренностях Linux, но то, что блок управления процессом имеет дополнительные поля, не означает, что эти объекты являются неотъемлемой частью концепции процесса.




Нет, просто совпадение и зависит от ОС, увеличьте номер петли до 10000 или более высокого числа,
for (int i = 0; i < 1000; i++)
вы получите совершенно другой результат.
synchronized нечестен в этом смысле, что означает, что нет никаких гарантий, какой поток войдет в синхронизированный раздел следующим, согласно спецификации.
Копнув глубже, вы обнаружите, что это поведение зависит от ОС, однако в любом случае вы должны полагаться на него, это может быть оптимизация, которая будет изменена в будущих версиях Oracle VM без какого-либо уведомления, поскольку она не указана, или для Например, это поведение может измениться из-за некоторых оптимизаций, выполненных во время выполнения, таких как lock coarsening.
Если вам нужен строгий порядок для доступа к критическому разделу, вам, вероятно, следует взглянуть на пакет java.util.concurrent, который содержит некоторые примитивы с опцией fair, например ReentrantLock.
Прежде всего, синхронизированное получение монитора - ничто по сравнению с запуском нового потока и выходом из спящего режима. Вы должны быть обеспокоены тем, насколько упорядоченной может быть ОС в отношении создания потоков и их планирования после выхода из спящего режима. Это имеет гораздо больший эффект, чем синхронизация.
Переписывание теста с 10 потоками каждые 100 мс и ожиданием 2000 мс, чтобы убедиться, что они прибывают в порядке 2..10 на заблокированном мониторе, вероятно, покажет что-то более или менее стабильное на данной ОС, но, вероятно, не переносимое поведение через ОС.
В большинстве систем потоки реализуются с помощью процессов. Так что это будет зависеть от базовой реализации вашей ОС. Возможно, вы захотите спросить разработчиков Ubuntu, показывают ли их семафоры / система блокировки какие-либо предпочтения в этом отношении. Может быть какой-то список / очередь для заблокированных процессов.