Turn: Java multi-threaded shared variable control

Java multi-threaded shared variable control

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/xiongyouqiang/article/details/77882385
  1. Visibility

If one thread changes to the shared values ​​of the variables, other threads can be seen in a timely manner, it called the visibility of shared variables. If a copy of the variables exist in working memory in multiple threads, this variable is called the shared variable

  1. JMM (java memory model)

When multiple threads simultaneously on a shared main memory variable to read and modify, it reads this variable to their working memory as a copy after copy of this to make changes, and then update back to the main memory where the variable local.

(Since the CPU time slice is the smallest unit of thread, so here working memory actually refers to the physical cache, local data acquisition CPU operations; and also refers to main memory is the memory, which is shared variables to store the original s position)

Two provisions:

a. Thread shared variables for all operations must be carried out in the working memory, you can not directly operate the main memory

b. between different threads can not access each other's working memory variables between threads pass variable values ​​to go through the main memory

If you modify a shared variable x thread 1 to thread 2 is visible, then, need to go through the following steps:

a. x values ​​will be changed to update the main memory thread 1

b. The main memory is updated value of x to the working copy of the updated memory of the threads 2 x

Therefore, to achieve the shared variable visibility must ensure that the following two points:

a. Thread-memory copy of the work of Change timely updates to the main memory

b. Other threads can prompt a shared variable on the main memory refresh to update the working copy of your own memory of that variable

Java can be achieved through shared variables synchronized, volatile, java concurrent class visibility

  1. synchronized achieve visibility

modify the code block is actually synchronized shared variables are incremented mutex to access multiple threads to access the synchronized code block, there is only one thread at a time to access and modify the contents of the code block (lock), when all the other threads waiting for this thread to leave the code block (lock release) have a chance to enter the synchronized code block.

Therefore, before and after a thread into a synchronized code block, the process is performed as follows:

a. thread to acquire a mutex

b. Clear the working memory

c. From the latest copy of the shared values ​​of main memory into the working memory variable replicas

d. Code Execution

e. a copy of the value of the modified refresh back to the main memory

f. thread releases the lock

The value of the shared variable Subsequently, other code when entering the synchronized code block, the memory is read to work on the latest value of a thread changes.

Performing a plurality of threads shared between a code block (modified access shared variables), since the cross-thread execution, the last value of the shared variable final result may have more:

public class SynchronizedTest {

    private boolean ready = false; private int result = 0; private int number = 1; public void write(){ ready = true; number = 2; } public void read(){if(ready){ result = number * 3; } System.out.println("result is " + result); } private class TestThread extends Thread{ private boolean flag; public TestThread(boolean flag){ this.flag = flag; } @Override public void run() { // TODO Auto-generated method stub if(flag){ write(); }else{ read(); } } } public static void main(String[] args){ SynchronizedTest test = new SynchronizedTest(); test.new TestThread(true).start(); test.new TestThread(false).start(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

As the code, since the two execution threads cross, the final result may be a result of 0 or 3 or 6

Shared variables are not visible mainly for the following reasons:

a. Cross-thread execution

b. reordering

c. failure to update the shared variable

By using synchronized ensure atomicity (synchronized or not the content execution code block, to ensure that all performed on finished) code and visibility, as modified in synchronized keyword added to read and write methods

  1. volatile achieve visibility (after jdk 1.5)

How volatile achieve visibility?

volatile variable is accessed each time a thread, the thread are forced to re-read the latest value of the variable from main memory, and when modifying the variable changes occur, the thread will be forced to refresh the latest value back to the main memory. As a result, different threads can see the latest value of the variable timely.

But we can not guarantee atomicity volatile variable changes:

Such as number ++, this operation is actually a collection of three operations (read number, number plus 1, the new value write-back number), volatile only guarantee is visible every step of the operation for all threads, but if two thread needs to perform number ++, so that a total of six sets of operations, is likely to cross between the execution, then the final number could cause results not expected.

So for this number ++ non-atomic operations, recommended synchronized:

synchronized(this){
     number++;   
}
  • 1
  • 2
  • 3

The following code: the final result is not necessarily the number 500, there may be less than 500, since a number is not ++ atomic operation, the visibility can not be guaranteed with the volatile

public class VolatileTest {

    public static int number = 0; public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } number++; } /** * @param args */ public static void main(String[] args) { final VolatileTest test = new VolatileTest(); for(int i = 0 ; i < 500 ; i++){ new Thread(new Runnable() { @Override public void run() { test.increase(); } }).start(); } //若当期依然有子线程没有执行完毕 while(Thread.activeCount() > 1){ Thread.yield();//使得当前线程(主线程)让出CPU时间片 } System.out.println("number is " + number); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

For non-atomic increment operation or the like, can ensure visibility by:

a. synchronized

b. ReentrantLock

c. AtomicInteger

synchronized amended as follows:

public void increase(){
        try {
            Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized(this){ number++; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

ReentrantLock modified as follows:

public class VolatileTest {

    public static int number = 0; public Lock lock = new ReentrantLock(); public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } lock.lock(); try{ number++;//这块的代码实际项目中可能会出现异常,所以要捕获 }finally{ lock.unlock();//用try finally块保证Unlock一定要执行 } } 。。。 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

AtomicInteger, an atomic operation provides a class Integer. In the Java language, ++ i and i ++ operation is not thread safe, when in use, will inevitably use the synchronized keyword. The AtomicInteger is by means of a thread-safe subtraction operator interface.

amend as below:

package com.mooc.test;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileTest { public static AtomicInteger number = new AtomicInteger(0); public void increase(){ try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } number.getAndIncrement();//获得当前值并且加1 } /** * @param args */ public static void main(String[] args) { final VolatileTest test = new VolatileTest(); for(int i = 0 ; i < 500 ; i++){ new Thread(new Runnable() { @Override public void run() { test.increase(); } }).start(); } //若当期依然有子线程没有执行完毕 while(Thread.activeCount() > 1){ Thread.yield();//使得当前线程(主线程)让出CPU时间片 } System.out.println("number is " + number.get()); } }

1

  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  1. volatile applicable

a. writing operation does not depend on the current value of variable

Such increment decrement, number = number + 5, etc. (not satisfied)

b. The current volatile variable is not dependent on other volatile variable

For example volatile_var> volatile_var2 this inequality (not satisfied)

  1. synchronized and volatile Compare

a. volatile synchronous operation is not required, it is more efficient and does not block the thread, but the application of relatively narrow

b. volatile read variable corresponding to the lock (i.e., into the synchronized code block), and write variables corresponding to the unlocking (exit synchronized code block)

. C synchronized shared variables can ensure visibility, can be secured within lock atomic operation; volatile visibility can only be guaranteed

Guess you like

Origin www.cnblogs.com/fearDoNothingOneLife/p/11095086.html