Java multi-threaded --CAS

If we now use a variable thread 20 constantly incremented by 1, as follows:

 1 public class ThreadDemo implements Runnable {
 2     private int data = 0;
 3     @Override
 4     public void run() {
 5         try {
 6             Thread.sleep(1000);
 7         } catch (InterruptedException e) {
 8             e.printStackTrace();
 9         }
10         data++;
11         //System.out.println(Thread.currentThread().getName() + "处理成功,data = " + data);
12     }
13 
14     public static void main(String[] args) {
15         ThreadDemo demo = new ThreadDemo();
16         for (int i = 0; i < 20; i++) {
17             new Thread(demo,"线程" + i).start();
18         }
19     }
20 }

Ideally i accumulated to 20, but the result of the actual operation is as follows:

1 thread 2 process is successful, Data = 3
 2 threads 3 process is successful, Data = 3
 3 Thread 1 process is successful, Data = 3
 . 4 thread 0 processed successfully, Data = 3
 5 5 thread processing is successful, Data =. 4
 . 6 threads 7 successfully processed , Data = 6
 . 7 threads 9 process is successful, Data = 8
 8 4 thread processing is successful, Data =. 5
 9 thread 6 processing is successful, Data = 4
 10 threads 8 process is successful, Data =. 7
 . 11 threads 10 process is successful, Data = 9
 12 is thread 11 successfully processed, Data = 11
 13 thread 13 process is successful, Data = 12
 14 thread 12 process is successful, Data = 11
 15 thread 14 process is successful, Data = 13
 16 thread 15 process is successful, Data = 14
 . 17 threads 19 successfully processed, Data = 18
 18 thread 18 process is successful, Data = 18
 . 1916 thread processing is successful, data = 16
 20 is threaded 17 process is successful, data = 16

Actual operating results may have a variety of situations, because there is such a security problem in the case of multi-threaded Java, leading to inaccurate results for this problem, there are several solutions

1, Option One: synchronized

 1 public class ThreadDemo implements Runnable {
 2 
 3     private int data = 0;
 4 
 5     private synchronized void increase(){
 6         data++;
 7     }
 8 
 9     @Override
10     public void run() {
11         try {
12             Thread.sleep(1000);
13         } catch (InterruptedException e) {
14             e.printStackTrace();
15         }
16         increase();
17         System.out.println(Thread.currentThread().getName() + "处理成功,data = " + data);
18     }
19 
20     public static void main(String[] args) {
21         ThreadDemo demo = new ThreadDemo();
22         for (int i = 0; i < 20; i++) {
23             new Thread(demo,"线程" + i).start();
24         }
25     }
26 }

Results are as follows:

1 thread 0 processed successfully, Data = 1
 2 Thread 1 process is successful, Data = 2
 3 Thread 2 process is successful, Data = 3
 4 threads 3 process is successful, Data = 4
 5 4 thread processing is successful, Data = 5
 . 6 thread 5 successfully processed , Data = 6
 7 thread 6 processing is successful, Data = 7
 . 8 thread 7 process is successful, Data =. 8
 . 9 threads 12 processes successful, Data = 13 is
 10 threads 14 process is successful, Data = 15
 . 11 threads 15 process is successful, Data = 16
 12 thread 16 successfully processed, Data =. 17
 13 thread 10 process is successful, Data = 11
 14 threads 9 process is successful, Data = 10
 15 threads 8 process is successful, Data = 10
 16 thread 13 process is successful, Data = 14
 . 17 threads 11 successfully processed, 13 is = Data
 18 is threaded 17 process is successful, data = 18
19 threads 18 process is successful, Data = 19
 20 is threaded 19 process is successful, data = 20

This time, the code is thread-safe, because we added a synchronized, that is, to let each thread to enter the increase () method come before attempting to lock the same time only one thread can lock, other threads need to wait for a lock. By doing this, it is possible to ensure that every time the accumulated change data 1, data disorder problem does not occur. However, such a simple data ++ operation, will be added synchronized lock to solve a heavy multi-threaded concurrency problems, one by one line, lock, data processing, release the lock, and then come in the next, a little overkill, can be synchronized solve more complex scenes and concurrent programming problems.

2, a more efficient scheme: Atomic Class

For the operation of such a simple data ++ class, java and offers a range of contract following classes Atomic atoms, for example of AtomicInteger, we can ensure the safety of the multi-threaded, concurrent updates a performance value, as follows:

 1 public class ThreadDemo implements Runnable {
 2 
 3     private AtomicInteger data = new AtomicInteger(0);
 4 
 5     @Override
 6     public void run() {
 7         try {
 8             Thread.sleep(1000);
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12         data.incrementAndGet();
13         System.out.println(Thread.currentThread().getName() + "处理成功,data = " + data);
14     }
15 
16     public static void main(String[] args) {
17         ThreadDemo demo = new ThreadDemo();
18         for (int i = 0; i < 20; i++) {
19             new Thread(demo,"线程" + i).start();
20         }
21     }
22 }

Results are as follows:

Thread 1 successfully processed, data = 2 
thread 0 processed successfully, Data = 2 
threads 2 process is successful, Data = 3 
4 thread processing is successful, Data = 5 
threads 3 process is successful, Data = 4 
thread 5 process is successful, Data = 6 
threads 6 treatment success, Data = 7 
threads 7 process is successful, Data =. 8 
threads 9 process is successful, Data = 11 
threads 11 process is successful, Data = 12 is 
the thread 13 process is successful, Data = 14 
threads 15 process is successful, Data = 16 
threads 16 successfully processed , data = 17 
threads 17 process is successful, data = 18 
threads 10 process is successful, data =. 11 
threads 8 process is successful, data =. 11 
threads 14 process is successful, data = 15 
threads 12 process is successful, data = 13 is 
the thread 18 process is successful, data = 19 
threads 19 process is successful, data= 20

Concurrent execution of multiple threads can incrementAndGet AtomicInteger's () method, meaning that the value of data is incremented by 1, then the latest cumulative return value, this code will not see the lock and release locks

1   public final int getAndAddInt(Object var1, long var2, int var4) {
2         int var5;
3         do {
4             var5 = this.getIntVolatile(var1, var2);
5         } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
6 
7         return var5;
8     }

 

In fact, Atomic atomic underlying class with the locking mechanism is not in the traditional sense, but no lock mechanism of the CAS, CAS through multi-threading mechanism to ensure the safety of a modified value, a bit optimistic locking means, the first value, cumulative It compares the original value if the time change, if the change will not be executed, will get the latest value again ( the Compare and the Set)

 3, Java8 optimization of the CAS: LongAdder

CAS lightweight mechanism can achieve thread-safe, but CAS also has a problem is to change the value of a variable while in a multithreaded cycle time may change several times before successfully, in the case of high concurrency of this case will be more common , spend a lot of system resources in an infinite loop above, therefore Java8 launched LongAdder

  1   /**
  2      * Adds the given value.
  3      *
  4      * @param x the value to add
  5      */
  6     public void add(long x) {
  7         Cell[] as; long b, v; int m; Cell a;
  8         if ((as = cells) != null || !casBase(b = base, b + x)) {
  9             boolean uncontended = true;
 10             if (as == null || (m = as.length - 1) < 0 ||
 11                 (a = as[getProbe() & m]) == null ||
 12                 !(uncontended = a.cas(v = a.value, v + x)))
 13                 longAccumulate(x, null, uncontended);
 14         }
 15     }
 16 
 17   /**
 18      * Handles cases of updates involving initialization, resizing,
 19      * creating new Cells, and/or contention. See above for
 20      * explanation. This method suffers the usual non-modularity
 21      * problems of optimistic retry code, relying on rechecked sets of
 22      * reads.
 23      *
 24      * @param x the value
 25      * @param fn the update function, or null for add (this convention
 26      * avoids the need for an extra field or function in LongAdder).
 27      * @param wasUncontended false if CAS failed before call
 28      */
 29     final void longAccumulate(long x, LongBinaryOperator fn,
 30                               boolean wasUncontended) {
 31         int h;
 32         if ((h = getProbe()) == 0) {
 33             ThreadLocalRandom.current(); // force initialization
 34             h = getProbe();
 35             wasUncontended = true;
 36         }
 37         boolean collide = false;                // True if last slot nonempty
 38         for (;;) {
 39             Cell[] as; Cell a; int n; long v;
 40             if ((as = cells) != null && (n = as.length) > 0) {
 41                 if ((a = as[(n - 1) & h]) == null) {
 42                     if (cellsBusy == 0) {       // Try to attach new Cell
 43                         Cell r = new Cell(x);   // Optimistically create
 44                         if (cellsBusy == 0 && casCellsBusy()) {
 45                             boolean created = false;
 46                             try {               // Recheck under lock
 47                                 Cell[] rs; int m, j;
 48                                 if ((rs = cells) != null &&
 49                                     (m = rs.length) > 0 &&
 50                                     rs[j = (m - 1) & h] == null) {
 51                                     rs[j] = r;
 52                                     created = true;
 53                                 }
 54                             } finally {
 55                                 cellsBusy = 0;
 56                             }
 57                             if (created)
 58                                 break;
 59                             continue;           // Slot is now non-empty
 60                         }
 61                     }
 62                     collide = false;
 63                 }
 64                 else if (!wasUncontended)       // CAS already known to fail
 65                     wasUncontended = true;      // Continue after rehash
 66                 else if (a.cas(v = a.value, ((fn == null) ? v + x :
 67                                              fn.applyAsLong(v, x))))
 68                     break;
 69                 else if (n >= NCPU || cells != as)
 70                     collide = false;            // At max size or stale
 71                 else if (!collide)
 72                     collide = true;
 73                 else if (cellsBusy == 0 && casCellsBusy()) {
 74                     try {
 75                         if (cells == as) {      // Expand table unless stale
 76                             Cell[] rs = new Cell[n << 1];
 77                             for (int i = 0; i < n; ++i)
 78                                 rs[i] = as[i];
 79                             cells = rs;
 80                         }
 81                     } finally {
 82                         cellsBusy = 0;
 83                     }
 84                     collide = false;
 85                     continue;                   // Retry with expanded table
 86                 }
 87                 h = advanceProbe(h);
 88             }
 89             else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
 90                 boolean init = false;
 91                 try {                           // Initialize table
 92                     if (cells == as) {
 93                         Cell[] rs = new Cell[2];
 94                         rs[h & 1] = new Cell(x);
 95                         cells = rs;
 96                         init = true;
 97                     }
 98                 } finally {
 99                     cellsBusy = 0;
100                 }
101                 if (init)
102                     break;
103             }
104             else if (casBase(v = base, ((fn == null) ? v + x :
105                                         fn.applyAsLong(v, x))))
106                 break;                          // Fall back on using base
107         }
108     }

 

LongAdder way is to try to use the CAS segments to improve performance of highly concurrent execution CAS operation, when an excessive number of concurrent threads to update its internal engage in a Cell arrays, with each segment is a value, then, make a lot of each thread to perform CAS-accumulate operation on different internal values ​​of Cell value, the CAS to calculate the pressure dispersion values ​​different segments Cell, so can greatly reduce multi-threaded infinite loop occurs when updating a value of the same problems, and other internal mechanisms that implement automatic segmentation migration, that is, if the value of a Cell implementation of CAS fails, it will automatically look for value in the value segment be another Cell CAS operations.

 1   /**
 2      * Returns the current sum.  The returned value is <em>NOT</em> an
 3      * atomic snapshot; invocation in the absence of concurrent
 4      * updates returns an accurate result, but concurrent updates that
 5      * occur while the sum is being calculated might not be
 6      * incorporated.
 7      *
 8      * @return the sum
 9      */
10     public long sum() {
11         Cell[] as = cells; Cell a;
12         long sum = base;
13         if (as != null) {
14             for (int i = 0; i < as.length; ++i) {
15                 if ((a = as[i]) != null)
16                     sum += a.value;
17             }
18         }
19         return sum;
20     }

Finally, the current accumulated total value acquired from LongAdder, the value will be the base value and all segments together Cell return

 

From LongAdder logic segments achieved locked in, we can also be modified for the optimistic concurrency some of the larger, longer duration of NA cache mode of buying projects, if there are 1000 stocks of goods, it is entirely possible inventory to split into 20 segments, you can build 20 fields in the inventory database table, such as stock_01, stock_02, and so on, in short, is to put your 1000 inventory to give him apart, each inventory segment is 50 stock, such stock 50 corresponds stock_01, stock_02 50 corresponds stock. Subsequently, 1000 requests per second over, by a simple random algorithm, each of the requests are random in stock in 20 segments, for a selected lock. This has up to 20 single execution request together, each at a single inventory segment lock request, then the service logic inside, that segment of the inventory database operations can be performed, including check stock -> determining whether Stock sufficient -> deductions inventory in this way to enhance concurrency, improve the user experience.

 

 

Guess you like

Origin www.cnblogs.com/ding-dang/p/11032247.html