Java High concurrency Five: JDK and contract 1 detailed introduction _java

Source: Internet
Author: User
Tags cas int size mutex semaphore static class stub

In the [high Concurrency Java II] Multithreaded Foundation, we have initially mentioned basic thread synchronization operations. This time, we will mention the synchronization control tool in the contract.

1. Use of various synchronization control tools

1.1 Reentrantlock

Reentrantlock sense is synchronized's enhanced version, synchronized is characterized by the use of simple, all to the JVM to deal with, but the function is relatively weak. Prior to JDK1.5, Reentrantlock performance was better than synchronized, and as the JVM was optimized, the performance of the current JDK versions was comparable. If it is a simple implementation, do not deliberately to use Reentrantlock.

Compared with the synchronized,reentrantlock, it has the features of Reentrant, interruptible, time-limited and fair lock, which is more abundant in function.

First, let's use an example to illustrate the most preliminary use of Reentrantlock:

Package test;

Import Java.util.concurrent.locks.ReentrantLock;

public class Test implements Runnable
{public
 static reentrantlock lock = new Reentrantlock ();
 public static int i = 0;

 @Override public
 Void Run ()
 {for
 (int j = 0; J < 10000000; J + +)
 {
 lock.lock ();
 Try
 {
 i++;
 }
 Finally
 {
 lock.unlock ();
 
 }}} public static void Main (string[] args) throws Interruptedexception
 {
 test test = new test ();
 thread T1 = new thread (test);
 Thread t2 = new Thread (test);
 T1.start ();
 T2.start ();
 T1.join ();
 T2.join ();
 System.out.println (i);
 }

}

There are two threads on the i + + operation, in order to ensure thread safety, the use of Reentrantlock, from the usage can be seen, compared with synchronized, reentrantlock a little more complex. Because it is necessary to unlock in Finally, if it is not finally unlocked, it is possible that the code may have an exception lock that is not released, and synchronized is the JVM that releases the lock.

So what are the outstanding features of Reentrantlock?

1.1.1 Can be reentrant

Single-threaded can be repeatedly entered, but to repeat exit

Lock.lock ();
Lock.lock ();
Try
{
 i++;
 
} 
Finally
{
 lock.unlock ();
 Lock.unlock ();
}

Since Reentrantlock is a reentrant lock, so you can repeatedly get the same lock, it has a lock-related fetch counter, if a thread with the lock to get the lock again, then get the counter to add 1, and then the lock needs to be released two times to get real release (re-lock). This mimics the semantics of the synchronized; If a thread enters a synchronized block protected by a monitor already owned by the thread, it allows the thread to proceed, not releasing the lock when the thread exits the second (or subsequent) synchronized block. The lock is released only if the thread exits the first synchronized block it enters into the monitor protection.

The public class child extends Father implements runnable{the
 final static child = The new Child ();//To ensure that the lock unique public
 STA tic void Main (string[] args) {for
 (int i = 0; i < i++) {
  new Thread (Child). Start ();
 }
 }
 
 Public synchronized void DoSomething () {
 System.out.println ("1child.dosomething ()");
 Doanotherthing (); Call other synchronized methods in your own class
 }
 
 private synchronized void doanotherthing () {
 super.dosomething ();// Invokes the Synchronized method
 System.out.println ("3child.doanotherthing ()") of the parent class;
 
 @Override public
 Void Run () {
 child.dosomething ();
 }
}
Class Father {public
 synchronized void dosomething () {
 System.out.println ("2father.dosomething ()");
 }
}

We can see that a thread enters a different synchronized method and does not release the lock that was obtained before. So the output is still in sequential output. So synchronized is also back into the lock

Output:

1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
1child.dosomething ()
2father.dosomething ()
3child.doanotherthing ()
...

1.1.2. can be interrupted

Unlike synchronized, Reentrantlock is responsive to interrupts. Interrupt related knowledge view [high concurrent Java II] Multithreading basics

The normal lock.lock () is not able to respond to interrupts, lock.lockinterruptibly () can respond to interrupts.

We simulate a deadlock scene and then use interrupts to handle the deadlock.

Package test;
Import Java.lang.management.ManagementFactory;
Import Java.lang.management.ThreadInfo;
Import Java.lang.management.ThreadMXBean;

Import Java.util.concurrent.locks.ReentrantLock;
 public class Test implements Runnable {public static Reentrantlock Lock1 = new Reentrantlock ();

 public static Reentrantlock Lock2 = new Reentrantlock ();

 int lock;
 Public Test (int lock) {this.lock = lock;
 @Override public void Run () {try {if (lock = 1) {lock1.lockinterruptibly ();
 try {thread.sleep (500);
 The catch (Exception e) {//Todo:handle Exception} lock2.lockinterruptibly ();
 else {lock2.lockinterruptibly ();
 try {thread.sleep (500);
 The catch (Exception e) {//Todo:handle Exception} lock1.lockinterruptibly (); } catch (Exception e) {//Todo:handle Exception} finally {if (Lock1.isheldbycurrentthread ()) {Lock1.unloc
 K ();
 } if (Lock2.isheldbycurrentthread ()) {lock2.unlock (); } System.out.println (Thread.CurrentThread ().GetId () + ": Thread Exit");
 } public static void Main (string[] args) throws interruptedexception {Test T1 = new test (1);
 Test t2 = new Test (2);
 Thread thread1 = new Thread (t1);
 Thread thread2 = new Thread (t2);
 Thread1.start ();
 Thread2.start ();
 Thread.Sleep (1000);
 Deadlockchecker.check ();
 Static class Deadlockchecker {private final static Threadmxbean Mbean = Managementfactory. Getthreadmxbean (); Final static Runnable Deadlockchecker = new Runnable () {@Override public void run () {//TODO auto-generated method
 Stub while (true) {long[] Deadlockedthreadids = Mbean.finddeadlockedthreads ();
 if (deadlockedthreadids!= null) {threadinfo[] Threadinfos = Mbean.getthreadinfo (deadlockedthreadids); For (Thread t:thread.getallstacktraces (). Keyset ()) {for (int i = 0; i < threadinfos.length; i++) {if (T.getid)
 = = Threadinfos[i].getthreadid ()) {t.interrupt ();
 try {thread.sleep (5000)}}}; catch (Exception e) {//Todo:handle exception}}};
 public static void Check () {Thread t = new thread (deadlockchecker);
 T.setdaemon (TRUE);
 T.start ();

 }
 }

}

The above code is likely to have deadlock, thread 1 gets lock1, thread 2 gets lock2, and then each other wants to get the other lock.

We use Jstack to see what happens when we run the above code

Did find a deadlock.

Deadlockchecker.check () method is used to detect deadlocks and then break the deadlock thread. After the interrupt, the thread exits normally.

1.1.3. Available for limited hours

Timeout does not get lock, returns false, does not permanently wait to constitute a deadlock

The Lock.trylock (long timeout, timeunit unit) is used to implement a time-limited lock, which is a time and an organization.

Give an example to illustrate the following:

Package test;

Import Java.util.concurrent.TimeUnit;
Import Java.util.concurrent.locks.ReentrantLock;

public class Test implements Runnable
{public
 static reentrantlock lock = new Reentrantlock ();

 @Override public
 Void Run ()
 {
 try
 {
 if (Lock.trylock (5, timeunit.seconds))
 {
 Thread.Sleep (6000);
 }
 else
 {
 System.out.println ("Get lock Failed");
 }
 catch (Exception e)
 {
 }
 finally
 {
 if (Lock.isheldbycurrentthread ())
 {
 Lock.unlock ();
 
 }} public static void Main (string[] args)
 {
 Test t = new Test ();
 thread T1 = new Thread (t);
 Thread t2 = new Thread (t);
 T1.start ();
 T2.start ();
 }



Use two threads to scramble for a lock, and when a thread gets the lock, sleep6 seconds, each thread tries only 5 seconds to get the lock.

So there must be a thread that can't get the lock. Unable to obtain after the direct exit.

Output:

Get lock failed

1.1.4. Fair lock

How to use:

Public Reentrantlock (Boolean fair)

public static Reentrantlock Fairlock = new Reentrantlock (true);

The general sense of the lock is unfair, not necessarily the first thread can get the lock first, then the thread later get lock. An unfair lock may produce starvation.

A fair lock means that the lock will ensure that the thread comes first and gets the lock first. Although a fair lock does not produce starvation, the performance of a fair lock can be much worse than an unjust lock.

1.2 Condition

The relationship between condition and Reentrantlock is similar to that of synchronized and object.wait ()/signal ()

The await () method causes the current thread to wait while releasing the current lock, and when the signal () or the Signalall () method is used in another thread, the thread will regain the lock and continue executing. Or you can jump out of wait when a thread is interrupted. This is similar to the Object.wait () method.

The awaituninterruptibly () method is essentially the same as the await () method, but it does not wait for the response to be interrupted. The Singal () method is used to wake up a thread that is waiting. The relative Singalall () method wakes all threads that are waiting. This is similar to the Obejct.notify () method.

This is no longer described in detail here. Give an example to illustrate:

Package test;

Import java.util.concurrent.locks.Condition;
Import Java.util.concurrent.locks.ReentrantLock;

public class Test implements Runnable
{public
 static reentrantlock lock = new Reentrantlock ();
 public static Condition Condition = Lock.newcondition ();

 @Override public
 Void Run ()
 {
 try
 {
 lock.lock ();
 Condition.await ();
 System.out.println ("Thread is going On");
 }
 catch (Exception e)
 {
 e.printstacktrace ();
 }
 Finally
 {
 lock.unlock ();
 }
 }
 
 public static void Main (string[] args) throws Interruptedexception
 {
 Test t = new Test ();
 Thread thread = new Thread (t);
 Thread.Start ();
 Thread.Sleep ();
 
 Lock.lock ();
 Condition.signal ();
 Lock.unlock ();
 }



The above example is very simple, let a thread await live, let the main thread to wake it. Condition.await ()/signal can only be used after the lock is obtained.

1.3.Semaphore

For a lock, it is mutually exclusive. It means that as long as I get the lock, no one can get it again.

For semaphore, it allows multiple threads to enter the critical section at the same time. It can be considered a shared lock, but the shared limit is limited, the amount is used up, and other threads that do not get the quota are blocked outside the critical area. When the limit is 1 o'clock, it's equal to lock.

Here's an example:

Package test;

Import Java.util.concurrent.ExecutorService;
Import java.util.concurrent.Executors;
Import Java.util.concurrent.Semaphore;


public class Test implements Runnable
{
 final semaphore semaphore = new semaphore (5);
 @Override public
 Void Run ()
 {
 try
 {
 semaphore.acquire ();
 Thread.Sleep ();
 System.out.println (Thread.CurrentThread (). GetId () + "Done");
 }
 catch (Exception e)
 {
 e.printstacktrace ();
 } finally {
 semaphore.release ();
 }
 }
 
 public static void Main (string[] args) throws Interruptedexception
 {
 Executorservice executorservice = Executors.newfixedthreadpool (a);
 Final Test t = new test ();
 for (int i = 0; i < i++)
 {
 executorservice.submit (t);
 }

}}

There is a thread pool of 20 threads, each thread goes to Semaphore license, Semaphore only 5 licenses, after running can see, 5 batch, batch of output.

Of course, a thread can also request multiple licenses at once

public void acquire (int permits) throws Interruptedexception

1.4 Readwritelock

Readwritelock is a feature-sensitive lock. Read and write are two different functions, read-read not mutually exclusive, read-write mutual exclusion, write-write mutually exclusive.

This design is to increase the amount of concurrency, but also to ensure data security.

How to use:

private static Reentrantreadwritelock readwritelock=new Reentrantreadwritelock ();
private static Lock Readlock = Readwritelock.readlock ();
private static Lock Writelock = Readwritelock.writelock ();

Detailed examples can look at Java to implement producer consumer issues and reader questions, and this is not the case.

1.5 Countdownlatch

Countdown timer
A typical scenario is a rocket launch. Before the launch of the rocket, in order to ensure foolproof, often have to carry out the inspection of various equipment, instruments. The engine will not fire until all the tests have been completed. This scenario is ideal for using Countdownlatch. It can make the ignition thread
, wait until all the check threads are complete, and then execute

How to use:

Static final Countdownlatch end = new Countdownlatch (10);
End.countdown ();
End.await ();

Diagram:

A simple example:

Package test;

Import Java.util.concurrent.CountDownLatch;
Import Java.util.concurrent.ExecutorService;
Import java.util.concurrent.Executors;

public class Test implements Runnable
{
 static final countdownlatch Countdownlatch = new Countdownlatch (a);
 Static final Test t = new Test ();
 @Override public
 Void Run ()
 {
 try
 {
 thread.sleep);
 System.out.println ("complete");
 Countdownlatch.countdown ();
 }
 catch (Exception e)
 {
 e.printstacktrace ();
 }
 }
 
 public static void Main (string[] args) throws Interruptedexception
 {
 Executorservice executorservice = Executors.newfixedthreadpool (ten);
 for (int i = 0; i < i++)
 {
 executorservice.execute (t);
 }
 Countdownlatch.await ();
 System.out.println ("End");
 Executorservice.shutdown ();
 }



The main thread must wait until all 10 threads have finished executing before outputting "end".

1.6 Cyclicbarrier

Similar to the Countdownlatch, it is also waiting for some threads to finish before execution. The difference with Countdownlatch is that this counter can be used repeatedly. For example, suppose we set the counter to 10. So after the first batch of 1 0 threads, the counter is zeroed, and then the next batch of 10 threads

How to use:

public Cyclicbarrier (int parties, Runnable barrieraction)

Barrieraction is the action that the system performs when the counter is counted once

Await ()

Diagram:

Here's an example:

Package test;

Import Java.util.concurrent.CyclicBarrier;
 public class Test implements Runnable {private String soldier;

 Private final cyclicbarrier cyclic;
 Public Test (String soldier, Cyclicbarrier cyclic) {this.soldier = soldier;
 This.cyclic = cyclic;
 @Override public void Run () {try {/) wait for all soldiers to cyclic.await ();
 DoWork ();
 Wait for all soldiers to complete the work cyclic.await ();
 catch (Exception e) {//TODO auto-generated catch block E.printstacktrace ();
 } private void DoWork () {//TODO auto-generated Method stub try {thread.sleep (3000);
 catch (Exception e) {//Todo:handle Exception} System.out.println (soldier + ": Done");
 public static class Barrierrun implements Runnable {Boolean flag;

 int n;
 Public Barrierrun (boolean flag, int n) {super ();
 This.flag = Flag;
 THIS.N = n;
 @Override public void Run () {if (flag) {SYSTEM.OUT.PRINTLN (n + "task complete");
 else {System.out.println (n + "set complete");
 Flag = true; Public STAtic void Main (string[] args) {final int n = 10;
 thread[] threads = new Thread[n];
 Boolean flag = false;
 Cyclicbarrier barrier = new Cyclicbarrier (n, New Barrierrun (flag, N));
 System.out.println ("set");
 for (int i = 0; i < n; i++) {System.out.println (i + "report");
 Threads[i] = new Thread (New Test ("Soldier" + I, barrier));
 Threads[i].start ();

 }
 }

}

Print results:

Collection
0 reports
1 reports
2 reports
3 reports
4 reports
5 reports
6 reports
7 Reports
8 reports
9 reports
10 Sets complete
Soldier 5:done
Soldier 7:done
Soldier 8:done
Soldier 3:done
Soldier 4:done
Soldier 1:done
Soldier 6:done
Soldier 2:done
Soldier 0:done
Soldier 9:done
10 Tasks completed

1.7 Locksupport

Provides thread blocking primitives

Similar to the suspend

Locksupport.park ();
Locksupport.unpark (t1);

Not easy to cause thread freezes compared with suspend

Locksupport thought, and semaphore a bit similar, there is a license inside, park time to take away this permission, Unpark when the permission to apply. So if Unpark before park, there would be no thread freezes.

The following code is the suspend sample code in high concurrency Java II multi-threaded basis, and deadlocks occur when using suspend.

Package test;

Import Java.util.concurrent.locks.LockSupport;
 
public class Test
{
 static Object u = new Object ();
 static Testsuspendthread T1 = new Testsuspendthread ("T1");
 static Testsuspendthread t2 = new Testsuspendthread ("T2");
 
 public static class Testsuspendthread extends Thread
 {public
 testsuspendthread (String name)
 {
  SetName (name);
 
 @Override public
 Void Run ()
 {
  synchronized (U)
  {
  System.out.println ("in" + GetName ());
  Thread.CurrentThread (). suspend ();
  Locksupport.park ();
 
 }} public static void Main (string[] args) throws Interruptedexception
 {
 t1.start ();
 Thread.Sleep (m);
 T2.start ();
T1.resume ();
T2.resume ();
 Locksupport.unpark (t1);
 Locksupport.unpark (T2);
 T1.join ();
 T2.join ();
 }


Using Locksupport, however, does not occur as a deadlock.

Other than that

Park () can respond to interrupts, but not throw exceptions. The result of the interrupt response is that the return of the park () function can get the interrupt flag from thread.interrupted ().

There are plenty of places in the JDK for Park, and of course the implementation of Locksupport is implemented using Unsafe.park ().

public static void Park () {
Unsafe.park (False, 0L);
}

The realization of 1.8 reentrantlock

below to introduce the implementation of the next reentrantlock, the implementation of Reentrantlock is mainly composed of 3 parts:

    1. CAS status
    2. Wait queue
    3. Park ()

There is a state variable in the parent class of Reentrantlock to indicate the status of the synchronization

/**
 * The synchronization state.
 * *
 private volatile int state;

Set state to acquire a lock through CAS operations, and if set to 1, the lock holder is given to the current thread

Final void Lock () {
  if (compareandsetstate (0, 1))
  Setexclusiveownerthread (Thread.CurrentThread ());
  else
  acquire (1);
 }

If the lock is unsuccessful, a request is made

Public final void acquire (int arg) {
 if!tryacquire (ARG) &&
  acquirequeued (Addwaiter (node.exclusive), ARG))
  selfinterrupt ();
 }

First, go to the application to try Tryacquire, because at this point another thread may have released the lock.

If you still don't apply for a lock, just addwaiter, meaning to add yourself to the waiting queue.

Private node Addwaiter (node mode) {
 node node = new Node (Thread.CurrentThread (), mode);
 Try the fast path of Enq; Backup to full Enq on Failure
 Node pred = tail;
 if (pred!= null) {
  Node.prev = pred;
  if (Compareandsettail (pred, node)) {
  pred.next = node;
  return node;
  }
 }
 Enq (node);
 return node;
 }

There will be several attempts to apply for a lock, if the application is not yet, it will be suspended

Private Final Boolean parkandcheckinterrupt () {
 Locksupport.park (this);
 return thread.interrupted ();
 }

Similarly, if in the unlock operation, is the release of the lock, and then Unpark, here is not specifically said.

2. Concurrent containers and typical source analysis

2.1 Concurrenthashmap

We know that HashMap is not a thread-safe container, and the easiest way to make hashmap into thread-safe is to use

Collections.synchronizedmap, it's a package for HashMap.

public static Map M=collections.synchronizedmap (New HashMap ());

Similarly, a similar approach is provided for List,set.

However, this approach is only suitable for situations where the concurrent volume ratio is small.

Let's look at the implementation of Synchronizedmap.

Private final map<k,v> m; Backing Map final Object mutex; 
  Object on which to synchronize Synchronizedmap (map<k,v> m) {if (m==null) throw new NullPointerException ();
  THIS.M = m;
 Mutex = this;
  } synchronizedmap (map<k,v> m, Object mutex) {this.m = m;
 This.mutex = Mutex;
 public int size () {synchronized (mutex) {return m.size ();}
 public Boolean IsEmpty () {synchronized (mutex) {return m.isempty ();}
 public boolean ContainsKey (Object key) {synchronized (mutex) {return M.containskey (key);}
 public boolean Containsvalue (Object value) {synchronized (mutex) {return m.containsvalue (value);}
 Public V get (Object key) {synchronized (mutex) {return m.get (key);}
 Public V-Put (K key, V value) {synchronized (mutex) {return M.put (key, value);}
 Public V-Remove (Object key) {synchronized (mutex) {return m.remove (key);} public void Putall (map<? extends K,? extends V> Map) {synchronized (mutex) {M.putall (MAP);
 public void Clear () {synchronized (mutex) {m.clear ()}

 }

It wraps the hashmap inside and then adds synchronized to each hashmap operation.

Since each method acquires the same lock (mutex), this means that operations such as put and remove are mutually exclusive and greatly reduce concurrency.

Now let's see how Concurrenthashmap is implemented.

Public V-Put (K key, V value) {
 segment<k,v> s;
 if (value = = null)
  throw new NullPointerException ();
 int hash = hash (key);
 Int J = (hash >>> segmentshift) & Segmentmask;
 if ((s = (segment<k,v>) unsafe.getobject  //nonvolatile; recheck
  (segments, (J << Sshift) + sbase)) = = NULL)//in ensuresegment
  s = ensuresegment (j);
 Return S.put (key, hash, value, false);
 }

Within the Concurrenthashmap there is a segment segment, which divides the large hashmap into segments (small hashmap), and then hashes the data on each segment, so that multiple threads on different segments of the hash operation must be thread safe, So only need to sync the same segment of the thread on it, so that the separation of the lock, greatly increased the concurrent volume.

It's a hassle to use concurrenthashmap.size because it counts each segment's data and, at this point, locks each segment and then makes data statistics. This is the small drawback of separating the locks, but the size method should be a method that will not be invoked at high frequencies.

In implementation, do not use synchronized and lock.lock but try to use Trylock, at the same time in the implementation of HashMap, also made a little optimization. I won't mention it here.

2.2 Blockingqueue

Blockingqueue is not a high-performance container. But it is a very good container for sharing data. is the typical realization of producers and consumers.

Diagram:

To see the Java implementation of producer consumer issues and readers write questions

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.