Sunday, July 9, 2017

Java Lock



Java lock is introduced in  syncronised indentifier in  java basics when we firstly learned . Along with it , there are some machaism like (notify/wait) to control lock in multi-thread environment. nowadays, I think seldom people like to use it as this lock can't control threads atomicly and the way to purposedly initialise a object in order to use syncronised block is stupid in some occasions eg:



private Object lock = new Object(); // what's the point ?
private boolean call() {
   sycronised(lock) {
    }
}

So Here i will introduce some new application level locks and JVM level locks which i used previously in my work.

A lock  = A wall to block other threads going in their way!

Application Level lock

ReentrantLock: this lock can be regared as a replace or a newer implementation to syncronised().


 private Lock lock = new ReentrantLock();
 public void method1(){
  try {  
   lock.lock(); // lock
                 System.out.println(Thread.currentThread().getName() + " into me                        thod1");
   Thread.sleep(1000);
                 System.out.println(Thread.currentThread().getName() + "out of m                        ethod1");
   Thread.sleep(1000);
  } catch (InterruptedException e) {
  } finally {
   lock.unlock(); //  you must unlock in finally 
  }
 }
 
 public void method2(){
  try {
   lock.lock();
                 System.out.println(Thread.currentThread().getName() + "into met                        hod2");
   Thread.sleep(2000);
                 System.out.println(Thread.currentThread().getName() + "out of m                        ethod2");
   Thread.sleep(1000);
  } catch (InterruptedException e) {
  } finally {
   lock.unlock();
  }
 }
 // main method
 public static void main(String[] args) {

  final b ur = new b();
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
                  // method1 is running(locked) method2 can't run at the same time,
    //because each thread is locked. the same as syncronised
   ur.method1();
   ur.method2();
   }
  }, "t1");
  t1.start();
  try {
   Thread.sleep(10);
  } catch (InterruptedException e) {}
 }

Previously, the wait/notify must be used along with syncronise identifier, now in ReentrantLock, we can use Condition to replace .


 private Lock lock = new ReentrantLock();
 private Condition condition = lock.newCondition();
 
 public void method1(){
  try {
   lock.lock();
                 System.out.println(Thread.currentThread().getName() + " waiting                        ");
   Thread.sleep(3000);
          System.out.println(Thread.currentThread().getName() + "release                         lock");
          condition.await(); // = wait, will release the lock!
                 System.out.println(Thread.currentThread().getName() +"continue                         run");
  } catch (Exception e) {
  } finally {
   lock.unlock();
  }
 }
 
 public void method2(){
  try {
   lock.lock();
   System.out.println(Thread.currentThread().getName() + "enter");
   Thread.sleep(3000);
                        System.out.println(Thread.currentThread().getName() + " signal                         to other  thread wake up");
                        // = notify,won't release lock,it will keep going,at the same                        time method1 also go
condition.signal();
                        System.out.print("continue going");
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   lock.unlock();
  }
 }
 
 // main method
 public static void main(String[] args) {
  
  final b uc = new b();
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
    uc.method1();
   }}, "t1");

  Thread t2 = new Thread(new Runnable() {
   @Override
   public void run() {
    uc.method2();
   }}, "t2");
  
  t1.start();
  t2.start();
 }

Of course, one lock can create many conditions ,and then use signalAll() to wake other threads up. = notifiyAll() in old implementation; ReentrantLock constructor can have a boolean param to pass:
                          Lock lock = new ReentrantLock(boolean isfair); 
Fair Lock : First in , First Lock; Default is nonfair lock which is more efficient!

ReentrantReadWriteLock : this lock is used in read more and write less situation, it follow (read-read) the lock is shared. (real-write & write-write)  the lock mutually exclusive.


 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
 private ReadLock readLock = rwLock.readLock();
 private WriteLock writeLock = rwLock.writeLock();
 
 public void read(){
  try {
   readLock.lock();
   System.out.println(Thread.currentThread().getName() + "enter");
   Thread.sleep(3000);
   System.out.println(Thread.currentThread().getName() + " out");
  } catch (Exception e) {
  } finally {
   readLock.unlock();
  }
 }
 
 public void write(){
  try {
   writeLock.lock(); // for exmplae , you want to persist some dat                        a into DB
   System.out.println(Thread.currentThread().getName() + "enter");
   Thread.sleep(3000);
   System.out.println(Thread.currentThread().getName() + "out");
  } catch (Exception e) {
  } finally {
   writeLock.unlock();
  }
 }
 
 public static void main(String[] args) {
  
  final b urrw = new b();
  
  Thread t1 = new Thread(new Runnable() {
   @Override
   public void run() {
    urrw.read();
   }}, "t1");
  
  Thread t2 = new Thread(new Runnable() {
   @Override
   public void run() {
    urrw.read();
   }}, "t2");
  
  Thread t3 = new Thread(new Runnable() {
   @Override
   public void run() {
    urrw.write();
   }}, "t3");
  
  Thread t4 = new Thread(new Runnable() {
   @Override
   public void run() {
    urrw.write();
   }}, "t4");
    
  
//  t1.start(); // read-read: t1 t2 enter simultanously 
//  t2.start();
  
//  t1.start(); // R 
//  t3.start(); // W // read-write: t1 start,t3 stop,t3 only starts after                t1 finishs
  
  t3.start();
  t4.start(); // write-write : the same as above


JVM Level Lock 

Object Head : Mark word, used to describe the object, when it will expire, age , GC label, lock record, thread id, bits ...;
Biased Locks : biased to the lock that preoccupied this thread. , Default : on ;  
-XX: + UseBiasedLocking, when threads are less competitive, it will increase the efficiency;

Vector() : this is thread safe as we all know and it is implemented in syncronised{} , so the efficiency is not good, however, if add biased lock, the efficiency will increase a lot; 

Spin Locks : in threads competitive environments , threads can be acquired immediately if use spin lock , it's unnecessary to stop the thread to acquire this lock. so try to write syncronised{} block short so that the spin speed will be faster , it saves the thread start and release time; SpinLock

Notes : when doing tuning , try biased lock first , if it fails , try spin lock. if it also fails, try application level locks; 

Some suggestions :
  1. try not to use syncronosed methods, but use syncronised{} blocks just inlcuding those specific lines which indeed need to be locked. don's lock unnecessary lines ;   
  2. Split big object into small objects to increase the parallelism to increase success rate of spin lock and biased lock; eg :  The ConcurrentHashMap and  Collections.syncronisedMap() internally , they divide the big map into lots of small maps called Segment<K,V>. 


No comments:

Post a Comment

Add Loading Spinner for web request.

when web page is busily loading. normally we need to add a spinner for the user to kill their waiting impatience. Here, 2 steps we need to d...