Home>

Why thread synchronization

Because when we have multiple threads accessing a variable or object at the same time,If there are both read and write operations in these threads,Can cause confusion in the value of the variable or the state of the object,As a result, the program is abnormal.For example, if a bank account is operated by two threads at the same time,One takes 100 yuan and one saves 100 yuan. Assume that the account originally had 0 blocks. If the withdrawal thread and the deposit thread occur at the same time,What will happen?Unsuccessful withdrawal,The account balance is 100. The withdrawal was successful,The account balance is 0. So which one is it?It's hard to tell.So multi-threaded synchronization is to solve this problem.

First, the code when not synchronized

bank.java
package threadtest;
/**
* @author ww
*
* /
public class bank {
 private int count=0;//account balance
 //Save money
 public void addmoney (int money) {
  count +=money;
  system.out.println (system.currenttimemillis () + "Save in:" + money);
 }
 //Withdraw money
 public void submoney (int money) {
  if (count-money<0) {
   system.out.println ("Out of balance");
   return;
  }
  count-= money;
  system.out.println (+ system.currenttimemillis () + "Remove:" + money);
 }
 //Inquire
 public void lookmoney () {
  system.out.println ("Account balance:" + count);
 }
}
syncthreadtest.java
package threadtest;
public class syncthreadtest {
 public static void main (string args []) {
  final bank bank=new bank ();
  thread tadd=new thread (new runnable () {
   @override
   public void run () {
    //todo auto-generated method stub
    while (true) {
     try {
      thread.sleep (1000);
     } catch (interruptedexception e) {
      //todo auto-generated catch block
      e.printstacktrace ();
     }
     bank.addmoney (100);
     bank.lookmoney ();
     system.out.println ("\ n");
    }
   }
  });
  thread tsub=new thread (new runnable () {
   @override
   public void run () {
    //todo auto-generated method stub
    while (true) {
     bank.submoney (100);
     bank.lookmoney ();
     system.out.println ("\ n");
     try {
      thread.sleep (1000);
     } catch (interruptedexception e) {
      //todo auto-generated catch block
      e.printstacktrace ();
     }
    }
   }
  });
  tsub.start ();
  tadd.start ();
 }
}

The code is simple,I will not explainLet's see how it works?Intercepted part of it,Is it messy?I can't read it.

Insufficient balance

Account balance:0

Insufficient balance

Account balance:100

1441790503354 Deposit:100

Account balance:100

1441790504354Deposit:100

Account balance:100

1441790504354 Take out:100

Account balance:100

1441790505355 Deposit:100

Account balance:100

1441790505355 Take out:100

Account balance:100

Second, the code when using synchronization

(1) Synchronization method:

That is, there are methods modified by the synchronized keyword. Since every object in java has a built-in lock,When decorating a method with this keyword,A built-in lock protects the entire method.Before calling this method,Need to acquire a built-in lock,Otherwise, it will be blocked.

Modified bank.java

Look at the results:

Insufficient balance

Account balance:0

Insufficient balance

Account balance:0

1441790837380 Deposit:100

Account balance:100

1441790838380 Take out:100

Account balance:0

1441790838380 Deposit:100

Account balance:100

1441790839381 Take out:100

Account balance:0

It felt instantly understandable.

Note:The synchronized keyword can also decorate static methods,If the static method is called at this time,Will lock the entire class

(2) Synchronization code block

That is, the statement block decorated with the synchronized keyword.Blocks decorated with this keyword are automatically added with built-in locks,To achieve synchronization

The bank.java code is as follows:

package threadtest;
/**
* @author ww
*
* /
public class bank {
 private int count=0;//account balance
 //Save money
 public void addmoney (int money) {
  synchronized (this) {
   count +=money;
  }
  system.out.println (system.currenttimemillis () + "Save in:" + money);
 }
 //Withdraw money
 public void submoney (int money) {
  synchronized (this) {
   if (count-money<0) {
    system.out.println ("Out of balance");
    return;
   }
   count-= money;
  }
  system.out.println (+ system.currenttimemillis () + "Remove:" + money);
 }
 //Inquire
 public void lookmoney () {
  system.out.println ("Account balance:" + count);
 }
}

The results are as follows:

Insufficient balance

Account balance:0

1441791806699 Deposit:100

Account balance:100

1441791806700 Take out:100

Account balance:0

1441791807699 Deposit:100

Account balance:100

The effect is similar to method one.

Note:Synchronization is an expensive operation.Therefore, you should minimize the content that is synchronized.It is usually not necessary to synchronize the entire method,Use synchronized code blocks to synchronize key code.

(3) using special domain variables (volatile) to achieve thread synchronization

a. The volatile keyword provides a lock-free mechanism for accessing domain variables

b. Using volatile to modify the domain is equivalent to telling the virtual machine that the domain may be updated by other threads

c. So every time the field is used, it must be recalculated,Instead of using the value in a register

d.volatile does not provide any atomic operations,It also cannot be used to modify variables of final type.

The bank.java code is as follows:

package threadtest;
/**
* @author ww
*
* /
public class bank {
 private volatile int count=0;//account balance
 //save money
 public void addmoney (int money) {
  count +=money;
  system.out.println (system.currenttimemillis () + "Save in:" + money);
 }
 //Withdraw money
 public void submoney (int money) {
  if (count-money<0) {
   system.out.println ("Out of balance");
   return;
  }
  count-= money;
  system.out.println (+ system.currenttimemillis () + "Remove:" + money);
 }
 //Inquire
 public void lookmoney () {
  system.out.println ("Account balance:" + count);
 }
}

How does it work?

Insufficient balance

Account balance:0

Insufficient balance

Account balance:100

1441792010959 Deposit:100

Account balance:100

1441792011960 Take out:100

Account balance:0

1441792011961Deposit:100

Account balance:100

Do n’t understand again?Mess again. Why is this?It is because volatile cannot guarantee atomic operations,So volatile cannot replace synchronized. In addition, volatile will organize the compiler to optimize the code,So you can't use it without using it.Its principle is to read from memory every time a thread wants to access a volatile decorated variable.Instead of reading from the cache,Therefore, the value of the variable accessed by each thread is the same.This guarantees synchronization.

(4) Thread synchronization using reentrant locks

Added a java.util.concurrent package in javase5.0 to support synchronization.The reentrantlock class is a reentrant, mutually exclusive lock that implements the lock interface. It has the same basic behavior and semantics as using the synchronized method and fast,And expanded its capabilities.

The common methods of the reenreantlock class are:

reentrantlock ():create a reentrantlock instance

lock ():acquire lock

unlock ():release the lock

Note:reentrantlock () also has a constructor that creates a fair lock.But because it can greatly reduce the efficiency of program operation,Not recommended

The bank.java code is modified as follows:

package threadtest;
import java.util.concurrent.locks.lock;
import java.util.concurrent.locks.reentrantlock;
/**
* @author ww
*
* /
public class bank {
 private int count=0;//account balance
 //Need to declare this lock
 private lock lock=new reentrantlock ();
 //save money
 public void addmoney (int money) {
  lock.lock ();//Lock
  try {
  count +=money;
  system.out.println (system.currenttimemillis () + "Save in:" + money);
  } finally {
   lock.unlock ();//Unlock
  }
 }
 //Withdraw money
 public void submoney (int money) {
  lock.lock ();
  try {
  if (count-money<0) {
   system.out.println ("Out of balance");
   return;
  }
  count-= money;
  system.out.println (+ system.currenttimemillis () + "Remove:" + money);
  } finally {
   lock.unlock ();
  }
 }
 //Inquire
 public void lookmoney () {
  system.out.println ("Account balance:" + count);
 }
}

How does it work?

Insufficient balance

Account balance:0

Insufficient balance

Account balance:0

1441792891934 Deposit:100

Account balance:100

1441792892935 Deposit:100

Account balance:200

1441792892954 Take out:100

Account balance:100

The effect is similar to the first two methods.

If the synchronized keyword meets the needs of the user,Just use synchronized because it simplifies the code. If you need more advanced features,Use the reentrantlock class. At this time, pay attention to releasing the lock in timeOtherwise there will be deadlocks,Usually release the lock in finally code

(5) Thread synchronization using local variables

The bank.java code is as follows:

package threadtest;
/**
* @author ww
*
* /
public class bank {
 private static threadlocal<integer>count=new threadlocal<integer>() {
  @override
  protected integer initialvalue () {
   //todo auto-generated method stub
   return 0;
  }
 };
 //save money
 public void addmoney (int money) {
  count.set (count.get () + money);
  system.out.println (system.currenttimemillis () + "Save in:" + money);
 }
 //Withdraw money
 public void submoney (int money) {
  if (count.get ()-money<0) {
   system.out.println ("Out of balance");
   return;
  }
  count.set (count.get ()-money);
  system.out.println (+ system.currenttimemillis () + "Remove:" + money);
 }
 //Inquire
 public void lookmoney () {
  system.out.println ("Account balance:" + count.get ());
 }
}

running result:

Insufficient balance

Account balance:0

Insufficient balance

Account balance:0

1441794247939 Deposit:100

Account balance:100

Insufficient balance

1441794248940 Deposit:100

Account balance:0

Account balance:200

Insufficient balance

Account balance:0

1441794249941 Deposit:100

Account balance:300

Looking at the operation results,At first it was foggy,Why only keep it,Don't let it go?Look at the principle of threadlocal:

If you use threadlocal to manage variables,Every thread that uses the variable gets a copy of the variable,The copies are independent of each other,In this way, each thread can modify its own variable copy without affecting other threads.Understand nowIt turns out that each thread runs a copy,In other words, deposit and withdrawal are two accounts,The names of knowledge are the same.So the above effect will happen.

threadlocal and synchronization

a.threadlocal and synchronization mechanism are both to solve the problem of access conflicts of the same variable in multiple threads

b. The former uses the method of "space for time", and the latter uses the method of "time for space"

  • Previous On the copy of objects and mutual assignment between objects in C ++
  • Next ASPNET MVC5 Website Development Display Article List (9)