Java Concurrency (4)-Synchronized and CAs

Source: Internet
Author: User
Tags cas instance method visibility volatile

Introduction

As we said in the previous article, volatile is guaranteed visibility, ordering, and "partial" atomicity through the lock command. However, in most concurrency problems, it is necessary to ensure the atomicity of the operation, volatile does not have this function, then need to use other means to achieve the purpose of thread safety, in Java programming, we can through the lock, synchronized keyword, As well as CAS operations to achieve thread-safe purposes.

Synchronized

In Java concurrency programming, to ensure that thread synchronization is most familiar to programmers is the Synchronized keyword, the most convenient synchronized keyword is that he does not need to display the release of management locks, greatly reducing the probability of programming errors.

In Java1.5 and previous versions, synchronized is not the best choice for synchronization, and because of the frequent blocking and wake-up threads in concurrency, it wastes a lot of resources on-line state switching, resulting in synchronized concurrency less than reentrantlock in some cases. In the Java1.6 version, many optimizations were made to synchronized, which greatly improved the performance of synchronized. As long as the synchronized can meet the usage environment, it is recommended to use synchronized instead of using Reentrantlock.

Three ways to use synchronized
    1. Modifies the instance method, locks the current instance, and obtains the lock of the current instance before entering the synchronization method.
    2. Modifies the static method, locks the current class object, and obtains the lock of the current class object before entering the synchronization method.
    3. Modifies the block of code, specifies the lock object, locks the given object, and obtains a lock on the given object before entering the synchronization code block.

These three ways to use everyone should be familiar with, one should note that the modification of the static method can be used in conjunction with the modification of the instance method, will not block, because one is a modified class class, a decorated instance object. The following example illustrates this point:

public static void Main (string[] args) throws Interruptedexception {Waitthread (); Notifythread ();}    private static Object lockobject = new Object ();        private static void Waitthread () {Thread watithread = new Thread (new Runnable () {@Override public void Run () {synchronized (lockobject) {System.out.println (thread.currentthr                                EAD (). GetName () + "Wait-before");                    try {TimeUnit.SECONDS.sleep (2);                Lockobject.wait ();                } catch (Interruptedexception e) {e.printstacktrace ();            } System.out.println (Thread.CurrentThread (). GetName () + "after-wait");    }}}, "Waitthread"); Watithread.start ();}        private static void Notifythread () {Thread watithread = new Thread (new Runnable () {@Override                 public void Run () {       Synchronized (lockobject) {System.out.println (Thread.CurrentThread (). GetName () + "Notify-before");                Lockobject.notify ();                try {TimeUnit.SECONDS.sleep (2);                } catch (Interruptedexception e) {e.printstacktrace ();            } System.out.println (Thread.CurrentThread (). GetName () + "after-notify");    }}}, "Notifythread"); Watithread.start ();} Waitthreadwait-before//notifythreadnotify-before//notifythreadafter-notify//waitthreadafter-wait

In the code we open two threads to lock the static method and the instance method separately, from the printout result we can see that these two threads are locked by different objects and can be executed concurrently.

The underlying principle of synchronized

Let's take a look at the compiled bytecode for a synchronized keyword:

if (null == instance) {       synchronized (DoubleCheck.class) {        if (null == instance) {               instance = new DoubleCheck();           }    }}



You can see that the Synchronized keyword adds the two commands, Monitorenter and Monitorexit, before and after the synchronization code block. The monitorenter instruction acquires the lock object, and if the lock object is acquired, the lock counter is incremented by 1, and the current thread is blocked if it is not acquired. The Monitorexit command releases the lock object while reducing the lock counter by 1.

Optimization of JDK1.6 for synchronized

JDK1.6 's optimization of synchronized is mainly embodied in the introduction of the concept of "biased lock" and "lightweight lock", while the synchronized lock can only be upgraded and not degraded:

Here I do not intend to explain in detail the implementation of each kind of lock, want to understand can refer to the "in-depth understanding of Java Virtual Machine", simply say their own understanding.

The idea of biased locking is that if a thread obtains a lock, it enters the bias mode from the lock-free mode, which is done through the CAS operation, and the thread entering the biased mode does not need to synchronize every time it accesses the lock's synchronous code block, unless there are other threads accessing the lock.

Biased locking improves the performance of those with synchronous but non-competitive code, which means that if your synchronization code block is accessed by the same thread for a long time, the bias lock is more efficient because he reduces the performance cost of repeatedly acquiring and releasing locks. If your synchronization code block is frequently accessed between multiple threads, you can use the parameter-xx:-usebiasedlocking to suppress biased lock generation and avoid switching between multiple lock states.

The biased lock optimizes the situation where only one thread enters the synchronization code block, and when multiple threads access the lock, the lock is promoted for lightweight locking.

The idea of lightweight locking is that when multiple threads enter a synchronized code block, multiple threads keep a lightweight lock when they are not competing, and acquire locks through CAs. In the event of competition, the CAS spin operation is first used to acquire the lock, the spin takes place in a very short time, there is a fixed spin count, and once the spin acquisition fails, it is upgraded to a heavyweight lock.

Lightweight locks optimize the situation where multiple threads enter the synchronization code block, and when multiple threads are not competing, the lock can be acquired through CAs to reduce lock state switching. When more than one thread competes, instead of blocking the thread directly, it attempts to acquire the lock through CAS spin, reducing the probability of blocking the thread, thus improving the performance of the synchronized lock.

Synchronized's waiting wake-up mechanism

Synchronized wake-up is achieved through the Notify/notifyall and wait three methods, the execution of these three methods must be in the synchronous code block or synchronous method, otherwise it will be an error.

The wait method is for the thread that is currently executing the code to wait, Notify/notifyall the same, and notifies the waiting code to continue execution, notify only notifies any waiting threads, Notifyall notifies all waiting threads. The wait method, unlike sleep, releases the lock for the current synchronization block, and notify does not release the lock when it notifies either of the waiting threads, and the lock is released only after the current synchronization code block finishes executing. The following code can illustrate this point:

public class Synchronizedtest {public static synchronized void Staticsynctest () {Thread thread = new Thread (NE                    W Runnable () {@Override public void run () {for (int i = 0; i < 3; i++) {                    System.out.println ("Staticsynctest");                    try {TimeUnit.SECONDS.sleep (1); } catch (Interruptedexception e) {//TODO auto-generated catch block E.prin                    Tstacktrace ();        }                }            }        });    Thread.Start (); } public synchronized void Nonstaticsynctest () {Thread thread = new Thread (new Runnable () {@Overrid e public void Run () {for (int i = 0; i < 3; i++) {System.out.println ("No                    Nstaticsynctest ");                    try {TimeUnit.SECONDS.sleep (1); } catch (Interruptedexceptione) {//TODO auto-generated catch block E.printstacktrace ();        }                }            }        });    Thread.Start (); }}public static void Main (string[] args) throws Interruptedexception {synchronizedtest synchronizedtest = new Synchron    Izedtest ();    Synchronizedtest.staticsynctest (); Synchronizedtest.nonstaticsynctest ();} Staticsynctest//nonstaticsynctest//staticsynctest//nonstaticsynctest//staticsynctest//nonstaticsynctest

The wait thread does not start immediately after the Notify thread notification in the code, and the wait thread begins execution after the notity thread finishes executing the synchronization code block release lock.

Cas

During the optimization of synchronized we saw a large number of CAS operations, CAS full name compare and Set (or compare and Swap), CAS contains three operands: memory location (V), original value (A), new Value (B). In a nutshell, a CAS operation is an atomic operation implemented by a virtual machine, the function of which is to replace the old value (a) with the new value (B), and if the old value (a) is not changed, the substitution succeeds and the replacement fails if the old value (a) has been changed.

This problem can be illustrated by the Atomicinteger class's self-increment code, which, when not in use, often does not get the expected value of 10000, because noncasi[0]++ is not an atomic operation.

private static void IntegerTest() throws InterruptedException {    final Integer[] noncasi = new Integer[]{ 0 };    for (int i = 0; i < 10; i++) {        Thread thread = new Thread(new Runnable() {            @Override            public void run() {                for (int j = 0; j < 1000; j++) {                    noncasi[0]++;                }            }        });        thread.start();    }        while (Thread.activeCount() > 2) {        Thread.sleep(10);    }    System.out.println(noncasi[0]);}//7889

When using Atomicinteger's Getandincrement method to achieve self-increment is equivalent to turning the casi.getandincrement () operation into an atomic operation:

private static void AtomicIntegerTest() throws InterruptedException {    AtomicInteger casi = new AtomicInteger();    casi.set(0);    for (int i = 0; i < 10; i++) {        Thread thread = new Thread(new Runnable() {            @Override            public void run() {                for (int j = 0; j < 1000; j++) {                    casi.getAndIncrement();                }            }        });        thread.start();    }    while (Thread.activeCount() > 2) {        Thread.sleep(10);    }    System.out.println(casi.get());}//10000

Of course, the Synchronized keyword can also be used to achieve the goal, but CAS operations do not need to lock unlock and switch thread state, more efficient.

Take a look at what casi.getandincrement () specifically did, before JDK1.8 Getandincrement was implemented like this (Incrementandget):

private volatile int value;public final int incrementAndGet() {    for (;;) {        int current = get();        int next = current + 1;        if (compareAndSet(current, next))            return next;    }}

By compareandset the variable increment, if the self-increment succeeds completes the operation, if the self-increment is unsuccessful, then spins the next self-increment, because the value variable is the volatile adornment, through the volatile visibility, each get () can obtain the newest value, This ensures that the self-increment operation will succeed after a certain number of spins.

In JDK1.8, the Getandaddint method is directly encapsulated into atomic operation, which is more convenient to use.

public final int getAndIncrement() {    return unsafe.getAndAddInt(this, valueOffset, 1);}

The CAS operation is the cornerstone of implementing Java and the contract, which he understands is relatively simple but also very important. Java and the contract is built on the basis of CAS operation and volatile, the list of the J.U.C package of some class support diagram:

Java Concurrency (4)-Synchronized and CAs

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.