Technical points:

1. Threads and processes:

Before you start, distinguish between processes and threads. A program needs at least one process, and a process needs at least one thread. A relationship is the general structure of a thread-> process-> program. So the thread is the smallest unit of the program execution flow, while the process is an independent unit of the system for resource allocation and scheduling. Everything we’ve discussed below is based on threads.

2. Several important methods of Thread:

Let’s take a look at some of the important methods of Thread. A, start() method, which is called to start executing the thread; B, stop() method, which is called to force the thread to terminate execution; C, join method, which is called to wait for the thread to end. D, sleep() method, call this method the thread into the wait. E, run() method, call this method directly execute the thread’s run() method, but the thread call start() method will also run the run() method, the difference is that one is run by the thread scheduling run() method, one is directly call thread’s run() method!!

What about wait() and notify()? Notice that wait() and notify() methods are Object methods, not Thread methods. Meanwhile, wait() and notify() are used in conjunction to indicate thread suspension and thread recovery, respectively.

Wait () frees the lock on an object and sleep() does not. There is a lot of data on these issues, so I won’t go into detail here.

3. Thread state:

Threads have a total of 5 states, through the introduction of the second knowledge point above, it is easy to understand.

New state: New thread object, not before the start() method is called

Ready state: The thread is ready when the start() method is called. This does not mean that the thread becomes the current thread when the start() method is called. It is ready until it becomes the current thread. It’s also worth noting that the thread will be in the ready state when it recovers from sleep and suspension.

Run state: The thread is set to the current thread and starts executing the run() method. The thread enters the running state

Blocked: A thread is paused, for example after calling sleep()

Dead state: Thread execution has ended

4. Lock type

Reentrant lock: All synchronized methods in the executing object do not have to acquire the lock again

Interruptible lock: Interruptible while waiting for a lock to be acquired

Fair lock: the lock is acquired according to the waiting time of the thread waiting for the lock. The long waiting time has the priority to acquire the lock

Read/write lock: split the resource into two parts when reading and writing. When reading, multiple threads can read together, and when writing, they must write synchronously

Synchronized vs. Lock

1. I classify the differences between the two into a table for your comparison:

2. When an exception occurs in thread execution, the JVM will ask the thread to release the Lock. In finally, the thread must release the Lock. Suppose thread A acquires the lock and thread B waits. If thread A is blocked, thread B will wait. Depending on the situation, A Lock can be acquired in several ways. Threads can not wait for the LOCK state can not determine the LOCK type can be reentrant non-interruptible non-fair reentrant can determine fair (both) performance a little synchronization a lot of synchronization. Perhaps, seeing how little we know about LOCK here, let’s move on to further learning about LOCK.

Lock details and Demo

The following is the source code of the Lock interface, after the author trimmed the results:

public interface Lock {

    /**
     * Acquires the lock.
     */
    void lock();

    /**
     * Acquires the lock unless the current thread is
     * {@linkplain Thread#interrupt interrupted}.
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * Acquires the lock only if it is free at the time of invocation.
     */
    boolean tryLock();

    /**
     * Acquires the lock if it is free within the given waiting time and the
     * current thread has not been {@linkplain Thread#interrupt interrupted}.
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * Releases the lock.
     */
    void unlock();
    
}

From the Lock interface we can see that there are mainly one method, whose function can be seen in the comments: Lock () : acquire the Lock and wait if the Lock is temporarily used

Unlock () : the lock is released

TryLock (): Note that the return type is Boolean, false if the lock was held when it was acquired, true otherwise

TryLock (Long Time, TimeUnit Unit) : In contrast to TryLock (), you give a time limit to wait for the parameter

LockInterruptibly () : This lock is obtained in such a way that if the thread enters a wait during the lock acquisition phase, it can be interrupted to do something else

This explains the “lock type (LockInterruptibly ())”, “lock state (TryLock ())” and other issues in the previous section, as well as the fact that I wrote “roughly that it is possible to try to obtain the lock and the thread does not wait” using “yes”.

The following is an example of the general use of Lock, note that ReentrantLock is an implementation of the Lock interface.

package com.brickworkers; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockTest { private Lock lock = new ReentrantLock(); Private void method(Thread, Thread){Lock. Lock (); Try {System.out.println(" Thread name "+ Thread.getName () + "The lock is obtained "); }catch(Exception e){ e.printStackTrace(); } finally {System.out.println(" ThreadName "+ Thread.getName () + "The lock is released "); lock.unlock(); } } public static void main(String[] args) { LockTest lockTest = new LockTest(); // Thread T1 = new Thread(new Runnable() {Override public void run() {LockTest.method (Thread.currentThread()); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { lockTest.method(Thread.currentThread()); } }, "t2"); t1.start(); t2.start(); }} // Execution: Thread name t1 acquired the lock // Thread name t2 acquired the lock // Thread name t2 acquired the lock // Thread name t2 released the lock

tryLock():

package com.brickworkers; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockTest { private Lock lock = new ReentrantLock(); Private void method(Thread, Thread){/* Lock. Lock (); Try {System.out.println(" Thread name "+ Thread.getName () + "The lock is obtained "); }catch(Exception e){ e.printStackTrace(); } finally {System.out.println(" ThreadName "+ Thread.getName () + "The lock is released "); lock.unlock(); }*/ if(Lock.TryLock ()){try {System.out.println(" ThreadName "+ Thread.getName () + "The lock is obtained "); }catch(Exception e){ e.printStackTrace(); } finally {System.out.println(" ThreadName "+ Thread.getName () + "The lock is released "); lock.unlock(); }else{System.out.println(" I am "+Thread.currentThread().getName()+" I am ");}else{System.out.println(" I am "+Thread.currentThread().getName()+" I am "); } } public static void main(String[] args) { LockTest lockTest = new LockTest(); // Thread T1 = new Thread(new Runnable() {Override public void run() {LockTest.method (Thread.currentThread()); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { lockTest.method(Thread.currentThread()); } }, "t2"); t1.start(); t2.start(); }} // Result: Thread name t2 has acquired the lock // I am t1, someone is holding the lock, I will not give it up // Thread name t2 has released the lock

I’m sure you all know how to use Lock, too, so I won’t go into TryLock (long time, TimeUnit) and LockInterruptibly (). The former mainly has a wait time and writes a wait time in the test code, while the latter is mainly waiting for interrupt and throws an interrupt exception, which is not commonly used, so you can study it in depth if you like to explore. ReentrantLock’s definition of a balanced lock is described in two paragraphs in the source code:

/**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

As you can see from the above source code, you can control whether the Lock is fair or not. Furthermore, the default Lock is not fair. Here is the constructor for ReentrantLock:

public ReentrantLock() { sync = new NonfairSync(); // Default Unfair Locking}

The tail record:

The author is mediocre, but the purpose of this blog in the introduction has all been met. This is only the author in the study process of summary and summary, such as the existence of incorrect, welcome everyone to point out criticism.

Extension learning: for the implementation of the Lock layer, you can refer to: click the Lock layer introduction blog

Two Synchronization Performance Tests: Check out the Two Synchronization Performance Tests blog

Bloggers added in March 2018:

Go back to your blog. Finding things that are not fully stated. Here is a supplement, because this blog is widely visited, so in order not to mislead you, try to introduce the correct expression:

1. The underlying implementation of the two locks:

Synchronized: We know that Java uses bytecode instructions to control programs (this does not include compilation of hotspot code into machine code). In the byte instruction, there is a block of code contained in synchronized, which will result in the execution of two sections of the process.



Java syncDemo. Class: SyncDemo. Class: SyncDemo.



So here’s a code snippet of bytecode instruction, not as hard as you might think. To get to the point, it is clear that synchronized is mapped to bytecode instructions by adding two additional instructions: Monitorenter and Monitorexit. When an executing thread encounters the monitorenter command, it tries to acquire the lock, and if it does, the lock count is +1. If it does not, the lock is blocked. When it encounters MonitoreXit, the lock counter is -1, and when the counter is 0, the lock is released.

So some of you are wondering, there are two monitorexit? Immediately answer this question: As stated in my previous post above, synchronized lock release has two mechanisms. One is release after execution; The other is to send an exception and the virtual machine releases it. The second MonitoreXit in the figure is the flow that is executed when an exception occurs, which is where I started by saying that “there are two flows.” Moreover, we can also see from the figure that on line 13, there is a goto instruction, which means that if the operation ends normally, it will jump to line 19.

Now, do you know synchronized very well? Let’s talk about Lock again.

Lock: The implementation of Lock is different from synchronized, which is a pessimistic Lock that is timid and afraid of being eaten, so it locks itself before it eats. The bottom layer of LOCK is actually the embodiment of CAS optimistic Lock, it doesn’t matter, others rob it to eat, it to take the food again, so it is very optimistic. If I have a chance, I will tell you more about the mechanisms under the Concurrent package. If asked in an interview, you will tell me that the underlying implementation is mainly based on volatile and CAS operations.

Now, what I really want to add at the end of this post is this: use synchronized instead of Lock whenever possible

What is the concept? Here’s an analogy: your name is JDK, you have a child named synchronized, and later, you adopt a child named Lock. At first, when Lock arrived in his new home, he was very well-behaved and sensible, outperforming synchronized in every way. You are happy, but deep inside you are a little sad that you don’t want your own child to be any less nice than an adopted one. At this point, you’re more educated about your biological child, and you want to prove that your biological child, synchronized, is no worse than your adopted child, Lock. (The blogger is just speaking figuratively)

What about education? In JDK 1.6~ JDK 1.7, synchronized16, 7 years old, you, as a father, optimized for him. What optimized for him?

1. Thread Spin and Adaptive Spin We know that Java ‘threads are mapped to the kernel, and that suspending and resuming threads can have a significant impact on overhead. And JDK officials have noticed that many threads acquire a lock in a short period of time while waiting for a lock, so they don’t need to suspend the thread while it waits, but instead let it loop aimlessly, typically setting it 10 times. This avoids the overhead of thread switching and greatly improves performance. Adaptive spins, which give spins a learning ability, don’t fix spins every 10 times. It can adjust its spin according to the spin of the thread in front of it, or even hang without passing through the spin.

2, lock elimination what is lock elimination? This is to remove unnecessary synchronization at compile time. So some friends are confused, I write my own code I will not know whether to lock here? I put a lock on it to indicate that there will be synchronization? No, lock elimination does not necessarily refer to lock elimination in your code. Let me use an analogy: Prior to JDK 1.5, String concatenation was implemented by StringBuffer (you can do this by writing a simple demo and looking at the bytecode instructions in the class file). After JDK 1.5, the String concatenation was implemented by StringBuffer. So it’s spliced with StringBuilder. Consider the previous case, such as the following code:

String str1="qwe";
String str2="asd";
String str3=str1+str2;

The underlying implementation would look like this:

StringBuffer sb = new StringBuffer();
sb.append("qwe");
sb.append("asd");

We know that StringBuffer is a thread-safe class, which means that both Append methods are in sync. After pointer escape analysis, we find that there is no thread-safe problem in this code, and we remove the synchronization lock at this point.

When using synchronized, we are all concerned about keeping the blocks of code as small as possible in order to avoid large overhead. So why are they bold? Continuing with the string concatenation example above, we know that in this code, each append needs to be synchronized once, so I can coarse the lock to the first append and the last append (don’t get bogged down in the previous lock undoing, I’m just talking figuratively).

Lightweight locks

5, biased lock