- Guide to the Volatile Keyword in Java
- Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
- 1. Overview
- 2. Shared Multiprocessor Architecture
- 3. Cache Coherence Challenges
- 3.1. Memory Visibility
- 3.2. Reordering
- 4. volatile Memory Order
- 5. volatile and Thread Synchronization
- 6. Happens-Before Ordering
- 6.1. Piggybacking
- 7. Conclusion
- Volatile java для чего
Guide to the Volatile Keyword in Java
Repeatedly, code that works in dev breaks down in production. Java performance issues are difficult to track down or predict.
Simply put, Digma provides immediate code feedback. As an IDE plugin, it identifies issues with your code as it is currently running in test and prod.
The feedback is available from the minute you are writing it.
Imagine being alerted to any regression or code smell as you’re running and debugging locally. Also, identifying weak spots that need attending to, based on integration testing results.
Of course, Digma is free for developers.
As always, the writeup is super practical and based on a simple application that can work with documents with a mix of encrypted and unencrypted fields.
We rely on other people’s code in our own work. Every day.
It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.
The problem is, of course, when things fall apart in production — debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky.
Lightrun is a new kind of debugger.
It’s one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics.
Learn more in this quick, 5-minute Lightrun tutorial:
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:
DbSchema is a super-flexible database designer, which can take you from designing the DB with your team all the way to safely deploying the schema.
The way it does all of that is by using a design model, a database-independent image of the schema, which can be shared in a team using GIT and compared or deployed on to any database.
And, of course, it can be heavily visual, allowing you to interact with the database using diagrams, visually compose queries, explore the data, generate random data, import data or build HTML5 database reports.
Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
1. Overview
Without necessary synchronizations, the compiler, runtime, or processors may apply all sorts of optimizations. Even though these optimizations are usually beneficial, sometimes they can cause subtle issues.
Caching and reordering are optimizations that may surprise us in concurrent contexts. Java and the JVM provide many ways to control memory order, and the volatile keyword is one of them.
This tutorial focuses on Java’s foundational but often misunderstood concept, the volatile keyword. First, we’ll start with some background about how the underlying computer architecture works, and then we’ll get familiar with memory order in Java. Further, we’ll understand the challenges of concurrency in multiprocessor shared architecture and how volatile help fix them.
2. Shared Multiprocessor Architecture
Processors are responsible for executing program instructions. Therefore, they must retrieve the program instructions and required data from RAM.
As CPUs can carry many instructions per second, fetching from RAM isn’t ideal for them. To improve this situation, processors use tricks like Out of Order Execution, Branch Prediction, Speculative Execution, and Caching.
This is where the following memory hierarchy comes into play:
As different cores execute more instructions and manipulate more data, they fill their caches with more relevant data and instructions. This will improve the overall performance at the expense of introducing cache coherence challenges.
We should think twice about what happens when one thread updates a cached value.
3. Cache Coherence Challenges
To expand more on cache coherence, we’ll borrow an example from the book Java Concurrency in Practice:
public class TaskRunner < private static int number; private static boolean ready; private static class Reader extends Thread < @Override public void run() < while (!ready) < Thread.yield(); >System.out.println(number); > > public static void main(String[] args) < new Reader().start(); number = 42; ready = true; >>
The TaskRunner class maintains two simple variables. Its main method creates another thread that spins on the ready variable as long as it’s false. When the variable becomes true, the thread prints the number variable.
Many may expect this program to print 42 after a short delay; however, the delay may be much longer. It may even hang forever or print zero.
The cause of these anomalies is the lack of proper memory visibility and reordering. Let’s evaluate them in more detail.
3.1. Memory Visibility
This simple example has two application threads: the main and the reader threads. Let’s imagine a scenario in which the OS schedules those threads on two different CPU cores, where:
- The main thread has its copy of ready and number variables in its core cache.
- The reader thread ends up with its copies, too.
- The main thread updates the cached values.
Most modern processors write requests that won’t be applied right after they’re issued. Processors tend to queue those writes in a special write buffer. After a while, they’ll apply those writes to the main memory all at once.
With all that being said, when the main thread updates the number and ready variables, there’s no guarantee about what the reader thread may see. In other words, the reader thread may immediately see the updated value, with some delay, or never at all.
This memory visibility may cause liveness issues in programs relying on visibility.
3.2. Reordering
To make matters even worse, the reader thread may see those writes in an order other than the actual program order. For instance, since we first update the number variable:
public static void main(String[] args)
We may expect the reader thread to print 42. But it’s actually possible to see zero as the printed value.
Reordering is an optimization technique for performance improvements. Interestingly, different components may apply this optimization:
- The processor may flush its write buffer in an order other than the program order.
- The processor may apply an out-of-order execution technique.
- The JIT compiler may optimize via reordering.
4. volatile Memory Order
We can use volatile to tackle the issues with Cache Coherence.
To ensure that updates to variables propagate predictably to other threads, we should apply the volatile modifier to those variables.
This way, we can communicate with runtime and processor to not reorder any instruction involving the volatile variable. Also, processors understand that they should immediately flush any updates to these variables.
This way, we communicate with runtime and processor to not reorder any instruction involving the volatile variable. Also, processors understand that they should immediately flush any updates to these variables.
5. volatile and Thread Synchronization
For multithreaded applications, we need to ensure a couple of rules for consistent behaviour:
- Mutual Exclusion – only one thread executes a critical section at a time
- Visibility – changes made by one thread to the shared data are visible to other threads to maintain data consistency
synchronized methods and blocks provide both of the above properties at the cost of application performance.
volatile is quite a useful keyword because it can help ensure the visibility aspect of the data change without providing mutual exclusion. Thus, it’s useful where we’re ok with multiple threads executing a block of code in parallel, but we need to ensure the visibility property.
6. Happens-Before Ordering
The memory visibility effects of volatile variables extend beyond the volatile variables themselves.
To make matters more concrete, suppose thread A writes to a volatile variable, and then thread B reads the same volatile variable. In such cases, the values that were visible to A before writing the volatile variable will be visible to B after reading the volatile variable:
Technically, any write to a volatile field happens-before every subsequent read of the same field. This is the volatile variable rule of the Java Memory Model (JMM).
6.1. Piggybacking
Because of the strength of the happens-before memory ordering, sometimes we can piggyback on the visibility properties of another volatile variable. For instance, in our particular example, we just need to mark the ready variable as volatile:
Anything prior to writing true to the ready variable is visible to anything after reading the ready variable. Therefore, the number variable piggybacks on the memory visibility enforced by the ready variable. Simply put, even though it’s not a volatile variable, it’s exhibiting a volatile behaviour.
Using these semantics, we can define only a few variables in our class as volatile and optimize the visibility guarantee.
7. Conclusion
In this article, we explored the volatile keyword, its capabilities, and the improvements made to it, starting with Java 5.
As always, the code examples can be found on GitHub.
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:
Volatile 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, но в таком случае получается поделка на тему синхронизации и не более. Бред какой-то..