Java multi-threading - Synchronized variables and concurrent access

In case of non-thread-safe too, concurrent access by multiple threads to the same object was an instance variable, resulting consequence is too dirty to read, that is, to have taken the data is actually a nice change.

Non-thread-safety issues exist in the "instance variables", if the variable is an internal method was private, "non-thread-safe," the problem does not exist.

1 Synchronized

1.1 synchronized方法

public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add1);
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    private Add a;
    public ThreadAA(Add add){
        this.a = add;
    }
    @Override
    public void run(){
        a.add("a");
    }
}

class ThreadBB extends Thread{
    private Add b;
    public ThreadBB(Add add){
        this.b = add;
    }
    @Override
    public void run(){
        b.add("b");
    }
}

class Add{
    private int num = 0;
    //同步方法
    synchronized public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

Print results

add a end
add b end
b name 200
a name 100
复制代码

From the results of the print order is not synchronized, but the cross, because the lock is an object lock keyword synchronized achieved . So the example above method, the first thread execution with synchronized keyword, the thread that holds the lock of the method belongs to the object, then other threads can only wait in a state if multiple threads access the same object.

Verify synchronized method of holding the lock for the object lock

//将上面的ThreadTest类中的main方法进行修改
public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
//        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add);
        threadBB.start();
    }
}
复制代码

operation result

add a end
a name 100
add b end
b name 200
复制代码

This time to see more results of the operation is the order of printing.

1.2 synchronized sync block

Mentioned above synchronization method, but using synchronized methods declared in some cases there are drawbacks, such as A thread calls a long time synchronization method to perform tasks, other threads must wait for a long time. In such cases, we can use the synchronized block synchronization code to solve, using synchronized synchronized block to wrap the code section must be performed synchronously.

public class ThreadFunction {
    public static void main(String[] args) {
        ObjFunction objFunction = new ObjFunction();
        FunA funA = new FunA(objFunction);
        funA.setName("a");
        funA.start();
        FunB funB = new FunB(objFunction);
        funB.setName("b");
        funB.start();

    }
}

class FunB extends Thread{
    private ObjFunction objFunction;
    public FunB(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class FunA extends Thread{
    private ObjFunction objFunction;
    public FunA(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class ObjFunction{
    public void objMethod(){
        try{
            System.out.println(Thread.currentThread().getName() + " start");
            synchronized (this) {
                System.out.println("start time = " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end time = "+ System.currentTimeMillis());
            }
            System.out.println(Thread.currentThread().getName() + " end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

operation result

a start
b start
start time = 1559033466082
end time = 1559033468083
a end
start time = 1559033468083
end time = 1559033470084
b end
复制代码

As can be seen, additional code sync block is asynchronous, the synchronization code blocks is performed synchronously. And synchronized (this) is locked object is the current object.

In addition to thisas a lock object, java also supports any object as a lock to synchronize function, but to note that as a synchronization monitor must be the same object, or else run the result is an asynchronous call.

1.3 synchronized static synchronized method

Synchronized keyword static can also be applied to the static method, so that a current corresponding Class * .java class file as a lock object.

Static synchronized method of holding the lock object = synchronized (class)

public class ThreadTest {
    public static void main(String[] args) {
        ThreadAA threadAA = new ThreadAA();
        threadAA.start();
        ThreadBB threadBB = new ThreadBB();
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    @Override
    public void run(){
        Add.add("a");
    }
}

class ThreadBB extends Thread{
    @Override
    public void run(){
        Add.add("b");
    }
}

class Add{
    private static int num = 0;
    //同步方法
    synchronized static public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

operation result

add a end
a name 100
add b end
b name 200
复制代码

1.4 synchronized类

Use the keyword synchronized modification of a class, the class methods are all synchronous method, in which the code does not show.

1.5 synchronized lock reentrant

It has synchronized reentrant lock function, i.e. synchronized in use, when a thread lock to give an object, the object lock request again when this is the object can be locked again. That is, when the internal synchronized method / code block to call a method of the present of other synchronized / block, you can always obtain.

public class ThreadAgain {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Service().service1();
            }
        }).start();
    }
}

class Service{
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized private void service2() {
        System.out.println("service2");
        service3();
    }

    synchronized private void service3() {
        System.out.println("service3");
    }

}
复制代码

operation result

service1
service2
service3
复制代码

2 volatile

The main role of the volatile keyword is visible in the variables between multiple threads.

It is mandatory to obtain the values of variables from the common stack, rather than the value of the variable acquired from private data stack thread. In multiple threads, stack and program counter is private, stack and global variables are working there.

Look at the code

public class MyVolatile {
    public static void main(String[] args) {
        try {
            RunThread runThread = new RunThread();
            runThread.start();
            Thread.sleep(2000);
            runThread.setRun(false);
            System.out.println("为runThread复制false");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

class RunThread extends Thread{
    private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

You can see from the console, the thread does not end. The problem is that the value of the private stack and the stack of work has a value of sync caused by trying to solve this problem you can use the volatile keyword.

Modify the class code RunThread

class RunThread extends Thread{
   volatile private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

Runs again, thread the end of normal.

Although the volatile keyword can be seen in the instance variables between multiple threads, but there is a fatal flaw volatile that does not support atomic.

Does not support authentication volatile atomicity

public class IsAtomic {
    public static void main(String[] args) {
        MyAtomicRun[] myAtomicRuns = new MyAtomicRun[100];
        for (int i = 0;i<100;i++){
            myAtomicRuns[i] = new MyAtomicRun();
        }
        for (int i = 0;i<100;i++){
            myAtomicRuns[i].start();
        }
    }
}

class MyAtomicRun extends Thread{
    volatile public static int count;
    private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

Print output

//篇幅较长,没有全部粘贴
count: 5000
count: 4900
count: 4800
count: 4700
count: 4600
count: 4500
count: 4400
count: 4400
复制代码

The results from the output of view, there is no output 10000 and our ideal state.

The code improvements

class MyAtomicRun extends Thread{
    volatile public static int count;
    //需要使用同步静态方法,这样是以class为锁,才能达到同步效果
    synchronized private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

Print output

count: 9300
count: 9400
count: 9500
count: 9600
count: 9700
count: 9800
count: 9900
count: 10000
复制代码

This time, the output is the correct result.

Where the main use of the volatile keyword in multiple threads can perceive instance variable is changed, and you can get the latest value is used, that is, you can get the latest value when reading shared variables multithreading.

2.1 Comparison of the volatile and synchronized

  1. volatile thread synchronization of lightweight implementation, so performance is certainly better than synchronized and volatile variables can only be modified, and synchronized methods, and code blocks can be modified.
  2. volatile blocking does not occur when multi-threaded access, and synchronized there will be blocked.
  3. to ensure the visibility of the volatile data, but not guaranteed atomicity; both synchronized and guarantee atomic, or indirectly ensure visibility, because the data is synchronized private memory and the common memory synchronization.
  4. The solution is volatile among multiple threads visibility of variables; and synchronized solution is to access resources between multiple threads synchronization.

2.2 Variable working memory

Like the above volatile keyword modified variables such operations ++ operator is actually not an atomic operation, it is not thread safe.

i ++ steps:

  1. I takes a value from memory
  2. I computing
  3. I will be written to memory

If the time in the second step of the calculation of another thread also changes the value of i, then the time will be stale data.

17138799-194122859a667229.png
image.png

<figcaption></figcaption>

  • read and load stages: working memory from the main memory copy variables to the current thread;
  • use and assign stages: execution code, change the value of shared variable;
  • store and write phases: the refresh memory corresponding to the variable value of the main data with the working memory.

In a multithreaded environment, use and assign are multiple occurrences, but this operation is not atomic, that is, after reading stage, if the main memory variable value is modified, the worker thread of memory as it has been over loaded , so it will not produce a corresponding change, causing the private memory and public memory variable values ​​are not synchronized, calculated and expected results is not the same, non-thread-safety problems.

For the volatile keyword modified variables, jvm only guaranteed value is loaded from the main memory to the working memory is up to date.

Reproduced in: https: //www.jianshu.com/p/bd8877ad99d8

Guess you like

Origin blog.csdn.net/weixin_33813128/article/details/91303300