The provisions of the synchronized

  • Shared variables must be flushed to main memory before the thread can be unlocked
  • The thread will clear the value of the working memory shared variable before locking, and needs to obtain the value of the shared variable from main memory.

The power of synchronized is not limited to mutually exclusive behavior, but also has another important aspect: memory visibility. Not only do we want to prevent one thread from using object state while another thread changes it, but we also want to ensure that when one thread changes object state, other threads see the change. Synchronization of threads does exactly that.

Built-in locks can be used to ensure that one thread sees the results of another thread’s execution in a predictable way. To ensure that all threads see the latest value of a shared variable, you can place the same lock on all threads that perform read or write operations. The following figure shows an example of synchronization visibility assurance.

When thread A executes A block of synchronized code, thread B then enters the block of synchronized code protected by the same lock. In this case, it is guaranteed that all variable values seen by thread A before the lock is released (including variables Y and X seen by thread A before the lock is released) will be seen by thread B after the lock is acquired. In other words, when thread B executes A lock protected block of synchronized code, it can see the result of all previous operations by thread A in the same lock protected block of synchronized code. If thread B enters lock M after thread A unlock M, then thread B can see what thread A did before unlock M and get I =1, j=1. If thread A enters lock M after thread B unlock M, then thread B does not necessarily see the operation in thread A, so the value of j does not have to be 1.

Synchronized Thread visibility security case

package com.keytech.task; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SynchronizedTestOne { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); Rumenzz r=new Rumenzz(); Executorservice.execute (()->{r.age (200); }); Executorservice.execute (()->{system.out.println (r.age ())); }); executorService.shutdown(); } } class Rumenzz{ private Integer age=0; public synchronized Integer getAge() { return age; } public synchronized void setAge(Integer age) { this.age = age; }}Copy the code

The code above is thread-safe, printing 0 or 200 because thread 1 and thread 2 are executed in a different order. To ensure consistency of results, the order of execution of threads needs to be controlled.

package com.keytech.task; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @synchronizedtestone * @description: TODO class description * @author: MAC * @date: 2021/1/1 **/ public class SynchronizedTestOne { public static void main(String[] args) { ExecutorService executorService  = Executors.newCachedThreadPool(); Rumenzz r=new Rumenzz(); CountDownLatch c=new CountDownLatch(1); executorService.execute(()->{ try { c.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(r.getAge()); }); executorService.execute(()->{ try { Thread.sleep(5000); c.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } r.setAge(200); }); Executorservice.shutdown (); } } class Rumenzz{ private Integer age=0; public synchronized Integer getAge() { return age; } public synchronized void setAge(Integer age) { this.age = age; }}Copy the code

Thread-safe output 200