Крупные компании, такие как bat, часто тестируют многопоточные Java-вопросы для интервью.

Java задняя часть Операционная система Безопасность

1. Расскажите о разнице между процессами, потоками и сопрограммами

Короче говоря, процесс является основной единицей работы программы и распределения ресурсов.У программы есть по крайней мере один процесс, а у процесса есть по крайней мере один поток.Процессы имеют независимые блоки памяти во время выполнения, в то время как несколько потоков совместно используют ресурсы памяти, уменьшая Чем больше число переключений, тем выше эффективность. Поток — это сущность процесса, базовая единица планирования и диспетчеризации процессора, а также базовая единица меньше, чем программа, которая может выполняться независимо. Несколько потоков в одном процессе могут выполнять одновременно.

2. Разбираетесь ли вы в потоке демона, чем он отличается от потока не демона?

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

3. Что такое многопоточное переключение контекста

Многопоточное переключение контекста относится к процессу переключения управления ЦП с уже запущенного потока на другой поток, готовый и ожидающий получения прав на выполнение ЦП.

4. Как создать два потока, в чем между ними разница?

Путем реализации java.lang.Runnable или расширения класса java.lang.Thread. Может быть лучше реализовать интерфейс Runnable, чем расширять Thread. Есть две причины:

  • Java не поддерживает множественное наследование, поэтому расширение класса Thread означает, что этот подкласс не может расширять другие классы, а класс, реализующий интерфейс Runnable, может также расширять другой класс.

  • Может потребоваться, чтобы класс был только исполняемым, поэтому наследование всего класса Thread было бы слишком дорого.

5. В чем разница между методами start() и run() в классе Thread?

Метод start() используется для запуска вновь созданного потока, а start() внутренне вызывает метод run(), что отличается от прямого вызова метода run(). Когда вы вызываете метод run(), он будет вызываться только в исходном потоке.Если новый поток не запущен, метод start() запустит новый поток.

6. Как определить, содержит ли поток объектный монитор

Класс Thread предоставляет метод holdLock(Object obj), который возвращает значение true тогда и только тогда, когда монитор объекта obj удерживается потоком.Обратите внимание, что это статический метод, что означает, что «поток» относится к текущий поток.

7. Разница между Runnable и Callable

Возвращаемое значение метода run() в интерфейсе Runnable недействительно, и он просто выполняет код в методе run(); метод call() в интерфейсе Callable имеет возвращаемое значение и является универсальным. type , который можно использовать вместе с Future и FutureTask для получения результата асинхронного выполнения. На самом деле это полезная функция, потому что важная причина, по которой многопоточность сложнее и сложнее, чем однопоточность, заключается в том, что многопоточность полна неизвестных.Выполнен ли поток? Как долго выполняется поток? Были ли назначены ожидаемые данные при выполнении потока? Нет никакого способа узнать, все, что мы можем сделать, это дождаться завершения этой многопоточной задачи. Однако Callable+Future/FutureTask может легко получить результат многопоточной операции и может отменить задачу потока, если время ожидания слишком велико и требуемые данные не получены.

8. Что вызывает блокировку потока

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

метод инструкция
sleep() sleep() позволяет указать период времени в миллисекундах в качестве параметра. Он заставляет поток переходить в состояние блокировки в течение указанного времени и не может получить процессорное время. По истечении указанного времени поток снова входит в исполняемое состояние. . Как правило, sleep() используется для ожидания готовности ресурса: после того, как тест обнаружит, что условие не выполнено, пусть поток заблокируется на некоторое время и повторит тестирование, пока условие не будет выполнено.
приостановить() и возобновить() Эти два метода используются вместе, suspend() переводит поток в заблокированное состояние и не будет автоматически возобновляться, для повторного входа потока в исполняемое состояние необходимо вызвать соответствующий метод возобновления(). Обычно приостановка() и возобновление() используются при ожидании результата, выданного другим потоком: после того, как тест обнаружит, что результат еще не получен, поток блокируется, а после того, как другой поток выдаст результат, вызовите возобновление (), чтобы возобновить его.
yield() yield() заставляет текущий поток отказываться от выделенного в данный момент процессорного времени, но не блокирует текущий поток, то есть поток все еще находится в состоянии выполнения и может быть снова выделен процессорным временем в любое время. Эффект от вызова yield() эквивалентен тому, что планировщик считает, что поток выполнил достаточно времени, чтобы переключиться на другой поток.
ждать() и уведомлять() Эти два метода используются вместе.Wait() переводит поток в состояние блокировки.Он имеет две формы.Один позволяет указать период времени в миллисекундах в качестве параметра, а другой не имеет параметров.Первый вызывается, когда соответствующий вызывается notify() или поток повторно входит в исполняемое состояние, когда заданное время превышено, и для последнего должен быть вызван соответствующий метод notify().

9. Разница между ожиданием(), уведомлением() и приостановкой(), возобновлением()

На первый взгляд они ничем не отличаются от методов suspend() и возобновить(), но на самом деле они совершенно разные. Суть отличия в том, что все описанные выше методы не освобождают занятый замок (если он занят) при блокировке, а эта пара методов наоборот. Вышеупомянутые основные различия приводят к ряду подробных различий.

Во-первых, все описанные выше методы относятся к классу Thread, а эта пара относится непосредственно к классу Object, то есть эта пара методов есть у всех объектов. На первый взгляд это очень невероятно, но на самом деле это очень естественно, потому что занятая блокировка будет освобождена, когда пара методов заблокируется, а блокировка принадлежит какому-либо объекту.Вызов метода wait() любого объекта вызовет поток, который нужно заблокировать, и блокировка этого объекта снимается. И вызов метода notify() любого объекта приводит к тому, что случайно выбранный из потоков, заблокированных вызовом метода wait() объекта, разблокируется (но фактически не выполняется, пока не будет получена блокировка).

Во-вторых, все описанные выше методы можно вызывать в любом месте, но эта пара методов должна вызываться в синхронизированном методе или блоке.Причина очень проста.Только в синхронизированном методе или блоке текущий поток держит блокировку, и есть замок.можно снять. Точно так же блокировка объекта, вызывающего эту пару методов, должна принадлежать текущему потоку, чтобы блокировку можно было снять. Следовательно, пара вызовов методов должна быть помещена в синхронизированный метод или блок, заблокированным объектом которого является объект, вызвавший пару методов. Если это условие не выполняется, программа все еще может быть скомпилирована, но во время выполнения возникнет исключение IllegalMonitorStateException.

Вышеупомянутые свойства методов wait() и notify() определяют, что они часто используются с ключевым словом synchronized. Сравнение между ними и механизмом межпроцессного взаимодействия операционной системы покажет их сходство: синхронизированные методы или блоки обеспечивают аналогичную функцию примитивы операционной системы, их выполнение не будет мешать механизму многопоточности, и эта пара методов эквивалентна примитивам блока и пробуждения (пара методов объявлена ​​как синхронизированная). Их комбинация позволяет реализовать ряд сложных алгоритмов взаимодействия между процессами (таких как алгоритмы семафоров) в операционной системе и решать различные сложные проблемы взаимодействия между потоками.

Два последних замечания о методах wait() и notify():

Во-первых: поток, разблокированный вызовом метода notify(), выбирается случайным образом из потоков, заблокированных вызовом метода wait() объекта.Мы не можем предсказать, какой поток будет выбран, поэтому будьте осторожны при программировании, чтобы избежать проблем из-за эта неопределенность.

Во-вторых: в дополнение к notify() существует также метод notifyAll(), который также может играть аналогичную роль, с той лишь разницей, что вызов метода notifyAll() заблокирует все потоки, заблокированные вызовом метода wait() класса объект одновременно Все разблокировано. Разумеется, в исполняемое состояние может перейти только тот поток, который получил блокировку.

Когда дело доходит до блокировки, мы должны говорить о взаимоблокировках.Проведя небольшой анализ, мы можем обнаружить, что как метод suspend(), так и вызов метода wait() без указания периода ожидания могут вызывать взаимоблокировки. К сожалению, Java не поддерживает предотвращение взаимоблокировок на уровне языка, и мы должны быть осторожны, чтобы избежать взаимоблокировок в программировании.

Выше мы проанализировали различные методы реализации блокировки потоков в Java, сосредоточившись на методах wait() и notify(), поскольку они являются наиболее мощными и гибкими в использовании, но это также приводит к тому, что они менее эффективны и более подвержены ошибкам. ошибки. В реальном использовании мы должны гибко использовать различные методы, чтобы лучше достичь нашей цели.

11. Условия, приводящие к взаимоблокировке

1. Условие взаимного исключения: ресурс может использоваться только одним процессом одновременно. 2. Условия запроса и удержания: когда процесс блокируется из-за запроса ресурсов, он будет удерживать полученные ресурсы. 3. Отсутствие условия депривации: Ресурсы, полученные процессом, не могут быть принудительно лишены до окончания использования. 4. Циклическое состояние ожидания: между несколькими процессами формируется циклическое отношение ресурсов ожидания.

12. Почему метод wait() и метод notify()/notifyAll() должны вызываться в синхронизированном блоке

Это обеспечивается JDK.Метод wait() и метод notify()/notifyAll() должны получить блокировку объекта перед вызовом В чем разница между методом wait() и методом notify()/notifyAll() при отказе от монитора объекта

Разница между методом wait() и методом notify()/notifyAll() при отказе от монитора объекта заключается в том, что метод wait() немедленно освобождает монитор объекта, а метод notify()/notifyAll() ожидает оставшийся код потока для завершения выполнения монитора объекта будет отброшен.

13. Разница между wait() и sleep()

Оба были подробно объяснены выше, поэтому вот краткое изложение:

  • sleep() происходит от класса Thread, а wait() происходит от класса Object.В процессе вызова метода sleep() поток не снимает блокировку объекта. И вызов потока метода ожидания снимет блокировку объекта.

  • sleep() не разрешает системные ресурсы после сна, ожидание позволяет другим потокам занимать ЦП

  • sleep(milliseconds) необходимо указать время сна, и он автоматически проснется, когда время истечет И wait() необходимо использовать с notify() или notifyAll()

14. Зачем ждать, методы nofity и nofityAll не помещаются в класс Thread

Очевидная причина заключается в том, что блокировки, предоставляемые JAVA, являются объектными, а не потоковыми, и каждый объект имеет блокировку, полученную потоком. Если потоку необходимо дождаться какой-либо блокировки, имеет смысл вызвать метод wait() для объекта. Если метод wait() определен в классе Thread, не очевидно, какой блокировки ожидает поток. Проще говоря, поскольку wait, notify и notifyAll являются операциями на уровне блокировки, они определены в классе Object, поскольку блокировки принадлежат объектам.

15. Как разбудить заблокированный поток

Если поток заблокирован из-за вызова метода wait(), sleep() или join(), вы можете прервать поток и разбудить его, вызвав InterruptedException; если поток сталкивается с блокировкой ввода-вывода, вы ничего не можете сделать, потому что ввод-вывод Реализована ли операционная система, код Java не имеет возможности напрямую прикасаться к операционной системе.

16. Что такое многопоточное переключение контекста

Многопоточное переключение контекста относится к процессу переключения управления ЦП с уже запущенного потока на другой поток, готовый и ожидающий получения прав на выполнение ЦП.

17. Разница между синхронизированным и ReentrantLock

Synchronized — это то же ключевое слово, что и if, else, for, while, а ReentrantLock — это класс, что является существенным различием между ними. Поскольку ReentrantLock является классом, он предоставляет более гибкие возможности, чем синхронизированный.Он может наследоваться, иметь методы и может иметь различные переменные класса.Расширяемость ReentrantLock по сравнению с синхронизированным отражается в нескольких моментах: (1) ReentrantLock может устанавливать время ожидания для получения блокировок, что позволяет избежать взаимоблокировок. (2) ReentrantLock может получать информацию о различных блокировках. (3) ReentrantLock может гибко реализовывать несколько уведомлений. Кроме того, механизмы блокировки у них на самом деле разные: нижний слой ReentrantLock вызывает метод Unsafe park для блокировки, а синхронизированная операция должна быть словом-меткой в ​​заголовке объекта.

18. Что такое FutureTask

На самом деле, как упоминалось ранее, FutureTask представляет собой асинхронную рабочую задачу. В FutureTask можно передать конкретный класс реализации Callable, который может ожидать получения результата задачи асинхронной операции, оценивать, завершена ли она, и отменять задачу. Конечно, поскольку FutureTask также является классом реализации интерфейса Runnable, FutureTask также можно поместить в пул потоков.

19. Что делать, если поток имеет исключение во время выполнения?

Если исключение не перехвачено, поток прекращает выполнение. Еще один важный момент: если этот поток удерживает монитор для определенного объекта, то монитор объекта будет немедленно освобожден

20. Какие виды блокировок есть в Java?

  • Spinlock: Spinlock включен по умолчанию после JDK1.6. Основываясь на предыдущих наблюдениях, состояние блокировки общих данных будет длиться только в течение короткого периода времени, и было бы напрасно приостанавливать и возобновлять потоки на этот короткий период времени, поэтому вот процесс, позволяющий потоку, запросившему заблокируйте позже Подождите некоторое время, но не отказывайтесь от времени выполнения процессора и посмотрите, можно ли быстро освободить поток, удерживающий блокировку. Чтобы заставить поток ждать, необходимо заставить поток выполнить цикл занятости, то есть операцию вращения. После jdk6 была введена адаптивная блокировка спина, то есть время ожидания больше не фиксируется, а определяется временем последнего спина на той же блокировке и состоянием владельца блокировки

  • Предвзятая блокировка: оптимизация блокировки, введенная после JDK 1. Цель состоит в том, чтобы исключить примитивы синхронизации для данных в ситуации отсутствия гонок. Дальнейшее улучшение производительности программы. Смещенная блокировка — это эксцентричное смещение, что означает, что блокировка будет смещена в сторону первого потока, который ее получит. еще раз. Синхронизировать. Смещенные блокировки могут повысить производительность программ с синхронизацией, но без конкуренции, что означает, что они не всегда полезны для работы программы.Если к большинству блокировок в программе обращаются несколько разных потоков, то смещенный режим является избыточным.Да, исходя из конкретного анализа конкретных проблем, можно решить, следует ли использовать необъективные блокировки.

  • Облегченная блокировка: чтобы снизить потребление производительности, вызванное получением и освобождением блокировок, вводятся «предвзятые блокировки» и «облегченные блокировки», поэтому в Java SE1.6 существует четыре состояния блокировки: состояние без блокировки, предвзятая блокировка. состоянии, облегченном состоянии блокировки и тяжелом состоянии блокировки, оно будет постепенно обостряться с конкуренцией. Замки могут быть улучшены, но не понижены, что означает, что смещенный замок не может быть понижен до смещенного замка после обновления до облегченного замка.

21. Как разделить данные между двумя потоками

Достаточно разделить объекты между потоками, а затем вызывать и ждать через wait/notify/notifyAll, await/signal/signalAll, например, блокирующая очередь BlockingQueue предназначена для обмена данными между потоками

22. Как правильно использовать функцию wait(), использовать if или while?

Метод wait() следует вызывать в цикле, потому что, когда поток получает процессор и начинает выполнение, другие условия могут не выполняться, поэтому лучше зациклиться, чтобы проверить, выполняются ли условия перед обработкой. Ниже приведен стандартный фрагмент кода, использующий методы ожидания и уведомления:

synchronized (obj) {
   while (condition does not hold)
     obj.wait(); // (Releases lock, and reacquires on wakeup)
     ... // Perform action appropriate to condition
}

23. Что такое локальная переменная потока ThreadLocal

Локальные переменные потока — это переменные, которые ограничены самим потоком, принадлежат самому потоку и не используются совместно несколькими потоками. Java предоставляет класс ThreadLocal для поддержки локальных переменных потока, что является способом достижения безопасности потока. Но будьте осторожны при использовании локальных переменных потока в управляемых средах (таких как веб-серверы), где время жизни рабочего потока больше, чем время жизни любой переменной приложения. Java-приложения подвержены риску утечки памяти, если какие-либо локальные переменные потока не освобождаются после выполнения работы.

24. Какова роль ThreadLoal?

Проще говоря, ThreadLocal — это способ обмена пространства на время. ThreadLocal.ThreadLocalMap поддерживается в каждом потоке для изоляции данных. Если данные не используются совместно, естественно, нет проблем с безопасностью потоков.

25. Какова роль модели производитель-потребитель?

(1) Повысить операционную эффективность всей системы за счет балансировки производственных мощностей производителей и потребительских мощностей потребителей, что является наиболее важной ролью модели производитель-потребитель. (2) Развязка, которая является побочным эффектом модели производитель-потребитель. Развязка означает, что между производителями и потребителями меньше связей. Чем меньше связей, тем более независимое развитие может быть без взаимных ограничений.

26. Напишите очередь производитель-потребитель

Это может быть реализовано путем блокировки очереди или ожидания-уведомления.Реализовано с использованием очереди блокировки

//消费者
public class Producer implements Runnable{
   private final BlockingQueue<Integer> queue;

   public Producer(BlockingQueue q){
       this.queue=q;
   }

   @Override
   public void run() {
       try {
           while (true){
               Thread.sleep(1000);//模拟耗时
               queue.put(produce());
           }
       }catch (InterruptedException e){

       }
   }

   private int produce() {
       int n=new Random().nextInt(10000);
       System.out.println("Thread:" + Thread.currentThread().getId() + " produce:" + n);
       return n;
   }
}
//消费者
public class Consumer implements Runnable {
   private final BlockingQueue<Integer> queue;

   public Consumer(BlockingQueue q){
       this.queue=q;
   }

   @Override
   public void run() {
       while (true){
           try {
               Thread.sleep(2000);//模拟耗时
               consume(queue.take());
           }catch (InterruptedException e){

           }

       }
   }

   private void consume(Integer n) {
       System.out.println("Thread:" + Thread.currentThread().getId() + " consume:" + n);

   }
}
//测试
public class Main {

   public static void main(String[] args) {
       BlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(100);
       Producer p=new Producer(queue);
       Consumer c1=new Consumer(queue);
       Consumer c2=new Consumer(queue);

       new Thread(p).start();
       new Thread(c1).start();
       new Thread(c2).start();
   }
}

Используйте ожидание-уведомление для достижения

Этот метод должен быть самым классическим, поэтому я не буду его здесь объяснять.

27. Что произойдет, если очередь пула потоков заполнена, когда вы отправляете задачу

Если вы используете LinkedBlockingQueue, то есть неограниченную очередь, не беда, продолжайте добавлять задачи в очередь блокировки на выполнение, потому что LinkedBlockingQueue можно рассматривать почти как бесконечную очередь, которая может хранить задачи бесконечно долго; если вы используете ограниченная очередь, например. Если используется ArrayBlockingQueue, задача сначала будет добавлена ​​в ArrayBlockingQueue. Когда ArrayBlockingQueue заполнена, вся задача будет обработана с использованием политики отклонения RejectedExecutionHandler. По умолчанию используется AbortPolicy.

28. Зачем использовать пул потоков

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

29. Какой алгоритм планирования потоков используется в java?

Упреждающий. После того, как поток исчерпает ресурсы ЦП, операционная система рассчитает общий приоритет в соответствии с приоритетом потока, голоданием потока и другими данными и назначит потоку следующий квант времени для выполнения.

30. Какова роль Thread.sleep(0)

Так как Java использует алгоритм упреждающего планирования потоков, может случиться так, что поток часто получает управление ЦП.Чтобы позволить некоторым потокам с более низким приоритетом получить управление ЦП, вы можете использовать Thread.sleep( 0) вручную инициировать операцию для операционной система для распределения временных интервалов, что также является операцией для балансировки управления процессором.

31. Что такое КАС

CAS, полное название Compare and Swap, означает «сравнить-заменить». Предположим, что есть три операнда: значение памяти V, старое ожидаемое значение A, значение, которое нужно изменить B, тогда и только тогда, когда ожидаемое значение A и значение памяти V совпадают, значение памяти будет изменено на B и вернуть true, иначе что? Конечно, CAS должен взаимодействовать с изменчивыми переменными, чтобы гарантировать, что переменная, полученная каждый раз, является последним значением в основной памяти, иначе старое ожидаемое значение A всегда будет постоянным значением A для потока, пока A Операция CAS завершается с ошибкой и никогда не может быть успешной

32. Что такое оптимистичная блокировка и пессимистичная блокировка

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

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

33. Что такое параллелизм ConcurrentHashMap?

Параллелизм ConcurrentHashMap — это размер сегмента, который по умолчанию равен 16, а это означает, что не более 16 потоков могут одновременно работать с ConcurrentHashMap, что также является самым большим преимуществом ConcurrentHashMap перед Hashtable. два потока для одновременного получения Hashtable data?

34. Как работает ConcurrentHashMap

Принцип реализации ConcurrentHashMap в jdk 1.6 и jdk 1.8 отличается.jdk 1.6:

ConcurrentHashMap является потокобезопасным, но способ достижения потокобезопасности отличается от Hashtablea. Hashtable блокируется, блокируя структуру хеш-таблицы.Когда поток занимает блокировку, другие потоки должны блокироваться и ждать, пока он освободит блокировку. ConcurrentHashMap использует отдельный метод блокировки, он блокирует не всю хеш-таблицу, а локальную блокировку, то есть когда поток занимает эту локальную блокировку, это не влияет на доступ других потоков к другим местам в хеш-таблице. Конкретная реализация: внутри ConcurrentHashMap есть сегмент.jdk 1.8

В jdk 8 ConcurrentHashMap больше не использует блокировки разделения сегментов, а использует CAS-алгоритм оптимистичной блокировки для достижения проблем с синхронизацией, но нижний уровень по-прежнему представляет собой реализацию «массив + связанный список -> красно-черное дерево».

37. Разница между CyclicBarrier и CountDownLatch

Эти два класса очень похожи.Они оба относятся к java.util.concurrent и могут использоваться для указания того, что код выполняется до определенного момента.Разница между ними заключается в следующем:

  • После того, как поток CyclicBarrier доходит до определенной точки, поток останавливается до тех пор, пока все потоки не достигнут этой точки, и все потоки не запустятся повторно; CountDownLatch этого не делает. только 1, поток продолжает работать

  • CyclicBarrier может вызывать только одну задачу, CountDownLatch может вызывать несколько задач.

  • CyclicBarrier можно использовать повторно, CountDownLatch нельзя использовать повторно, а значение счетчика равно 0, CountDownLatch больше нельзя использовать.

39. Безопасен ли поток оператора ++ в java?

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

40. Каковы ваши рекомендации по многопоточной разработке?

  • назвать тему

  • Минимизировать диапазон синхронизации

  • Предпочитаю изменчивый

  • Используйте как можно больше инструментов параллелизма более высокого уровня вместо ожидания и уведомления () для реализации потоковой связи, таких как BlockingQueue, Semeaphore

  • Предпочитайте параллельные контейнеры синхронным контейнерам.

  • Рассмотрите возможность использования пула потоков

О ключевом слове volatile

1. Можно ли создать массив Volatile?

В Java можно создавать массивы volatile-типов, но только как ссылку на массив, а не на весь массив. При изменении массива, на который указывает ссылка, он будет защищен volatile, но если несколько потоков одновременно изменяют элементы массива, то volatile-идентификатор не может играть прежнюю защитную роль.

2. Может ли volatile сделать неатомарную операцию атомарной?

Типичным примером является наличие в классе переменной-члена типа long. Если вы знаете, что к переменной-члену будут обращаться несколько потоков, таких как счетчик, цена и т. д., вам лучше сделать ее изменчивой. Зачем? Поскольку чтение длинной переменной в Java не является атомарным и должно быть разделено на два этапа, если один поток изменяет значение длинной переменной, другой поток может видеть только половину значения (первые 32 бита). Но чтение и запись в volatile long или double являются атомарными.

Одна из практик состоит в том, чтобы декорировать длинные и двойные переменные с помощью volatile, чтобы их можно было читать и записывать атомарно. И double, и long имеют ширину 64 бита, поэтому чтение этих двух типов делится на две части, сначала считываются первые 32 бита, а затем считываются оставшиеся 32. Этот процесс не является атомарным, однако чтение и запись volatile long или double переменные в Java являются атомарными. Другая роль изменчивого фиксажа состоит в обеспечении барьера памяти, например, используемого в распределенных средах. Проще говоря, модель памяти Java вставляет барьер записи перед записью изменчивой переменной и вставляет барьер чтения перед чтением изменчивой переменной. Это означает, что когда вы пишете volatile поле, вы можете гарантировать, что любой поток может увидеть записываемое вами значение, и в то же время, перед записью, вы также можете гарантировать, что любое обновление значения будет видно всем потокам, потому что барьер памяти will Все остальные записываемые значения обновляются в кэш.

3. Какие гарантии дают переменные типа volatile?

Volatile имеет две основные функции: 1. Предотвращение перестановки инструкций 2. Гарантия видимости.Например, JVM или JIT будут переупорядочивать операторы для повышения производительности, но переменные типа volatile назначаются даже без синхронизированных блоков. Они также не будут переупорядочены с другими операторами. Volatile обеспечивает гарантии того, что изменения, сделанные одним потоком, видны другим потокам. В некоторых случаях volatile также может обеспечивать атомарность, например чтение 64-битных типов данных, таких как long и double, не являются атомарными (младшие 32 бита и старшие 32 бита), но volatile типы double и long являются атомарными.

Просканируйте WeChat и обратите внимание на общедоступную учетную запись [хорошо изучите java] и поймите качественные статьи с первого раза.