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 :
- 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 ;
- 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