What the definition of a technical person, is a steel straight men, or emotional quotient is extremely low, can’t talk, or focus on technical output, full of the charm of seriously, we say things always two sides to everything, in fact there are many things, if you can’t have god’s perspective it’s hard to do one thing, and we can do is try to have this kind of perspective, a comprehensive look at problems, This requires a great store of knowledge.
Starting today, I’m going to write something about concurrent programming. I’ve actually used concurrent programming, but it’s really rare. I spend most of my time in CRUD, writing for the general public, and I only spend 10% of my time writing concurrent stuff.
1 Functions and usage scenarios of volatile
Volatile handles visibility of variables between threads and prevents instruction reordering. Verify this in code
/** ** Thread A and thread B are both running and making changes to the same variable, but since variables are not visible between threads, thread B's changes do not affect * this code will always output if you are bad, you will not output hello (without modification) * Correction is not impossible @param args */ public static void main(String[] args) {option.empty (); final VT vt = new VT(); Thread Thread01 = new Thread(vt); Thread Thread02 = new Thread(new Runnable() { public void run() { try { Thread.sleep(3000); } catch (InterruptedException ignore) { } vt.sign = true; System.out.println("vt. Sign = true while (! Sign) over!" ); }}); Thread01.start(); Thread02.start(); }Copy the code
}
/** * Volatile principle * Memory is actually divided into one main memory and each additional thread opens up a minute memory, Variables between the two threads are stored on their own values. * If volatile is not used, the A and B working memory have separate values of sign, one of which is true and the other is false. Changes in the sign attribute do not modify main memory Thread B's memory will fetch the latest main memory, thus implementing the visibility of variables between threads. By looking at the assembly instruction, we found that the lock instruction is a memory barrier, which guarantees the following three things: * * Writes this processor's cache to memory. * Subsequent instructions cannot be reordered to the location in front of the memory barrier. * If it is a write action, the corresponding memory in other processors will be invalid. The addition of volatile does not guarantee atomicity
class VT implements Runnable {
private Logger logger = LoggerFactory.getLogger(Volidate.class);
/ / verification volatile
public volatile boolean sign = false;
public void run() {
while (true) {
if (sign) {
logger.info("Hello");
} else {
logger.info("You bad"); }}}}Copy the code
Use scenarios, such as singleton mode
** * Validating order * volatile not only has interthread visibility * but also prevents instruction reordering, which sync does not have */ publicclass Singleton {
private Singleton(){}/** * prevent instruction reordering */
private volatile static Singleton instance;
public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
// Command rebeats will make this judgment wrong
if (instance == null) {
instance = newSingleton(); }}}returninstance; }}Copy the code
How does volatile implement instruction rearrangement
By looking at the assembly instructions, it is found that there is a lock in the assembly execution. This lock finally operates on the memory barrier and achieves the purpose of preventing instruction rearrangement
When does an instruction reorder occur
Take a chestnut
instance= newSingleton(), which is an atomic operation, can actually be abstracted as the following JVM instructions memory =allocate();//1: allocates memory space for the object
ctorInstance(memory); //2: initializes the object
instance =memory; //3: Sets instance to the newly allocated memory address
Copy the code
Operation 2 depends on operation 1, but operation 3 does not depend on operation 2, so the JVM can optimally reorder instructions for them as follows:
memory =allocate(); //1: allocates memory space for the object
instance =memory; //3: instance refers to the newly allocated memory address, while the object is not initialized
ctorInstance(memory); //2: initializes the object
Copy the code
You can see that after the instruction rearrangement, instance pointing to the allocated memory is placed first, and initialization of the memory is placed second.
Said, now found that actually volatile is prepared for multithreaded, but he is not for high concurrency, because he has no atomicity, using his scene, must be independent of his current value, or with his prevent instruction rearrangement function, of course rearrangement is equivalent to prevent instructions also prevents the JVM to optimize some consumption performance.