Horse Soldier High Concurrency Programming Series Record
https://www.bilibili.com/video/av11076511/?spm_id_from=333.788.videocard.4
ReentrantLock1
reentrantlock used instead of synchronized
In this example, because m1 locks this, m2 cannot execute until m1 has finished executing
Here is a review of the original semantics of synchronized
import java.util.concurrent.TimeUnit; public class ReentrantLock1 { synchronized void m1() { //this object is locked for(int i=0; i<10; i++) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } } synchronized void m2() { System.out.println("m2 ..."); } public static void main(String[] args) { ReentrantLock1 rl = new ReentrantLock1(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); } }
ReentrantLock2
reentrantlock used instead of synchronized
Because m1 locks this, m2 cannot execute until m1 has finished executing
The same functionality can be accomplished with reentrantlock
It is important to note that the lock must be released manually
If an exception is encountered with synchronized locking, the jvm automatically releases the lock, but the lock must be released manually, so it is often released in finally
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLock2 { Lock lock = new ReentrantLock(); void m1() { try { lock.lock(); //Can be written outside of try //ReentrantLock requires manual locking equivalent to synchronized(this) for (int i = 0; i < 10; i++) { TimeUnit.SECONDS.sleep(1); System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//ReentrantLock also needs to release the lock manually } } void m2() { lock.lock(); System.out.println("m2 ..."); lock.unlock(); } public static void main(String[] args) { ReentrantLock2 rl = new ReentrantLock2(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); } }
ReentrantLock3 (tryLock)
Using reentrantlock, you can try to lock tryLock so that it cannot be locked or locked for a specified time, and the thread can decide whether to continue waiting
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLock3 { Lock lock = new ReentrantLock(); void m1() { try { lock.lock(); for (int i = 0; i < 10; i++) { TimeUnit.SECONDS.sleep(1); System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /* Attempting to lock using tryLock will continue the method regardless of whether it is locked or not Locking can be determined by the return value of tryLock You can also specify a time for tryLock, because tryLock(time) throws an exception, so be aware that unclock processing must be placed in finally */ void m2() { /* //First boolean locked = lock.tryLock();//Try to get it, and the return value is true, otherwise it is false System.out.println("m2 ..." + locked); if(locked) lock.unlock(); */ //Second boolean locked = false; try { locked = lock.tryLock(5, TimeUnit.SECONDS); System.out.println("m2 ..." + locked); } catch (InterruptedException e) { e.printStackTrace(); } finally { if(locked) lock.unlock(); } } public static void main(String[] args) { ReentrantLock3 rl = new ReentrantLock3(); new Thread(rl::m1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rl::m2).start(); } }
ReentrantLock4 (interruptable)
Using ReentrantLock, you can also call the lockInterruptibly method, which responds to the thread interrupt method.
Can be interrupted while a thread is waiting for a lock
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; public class ReentrantLock4 { public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread(()->{ try { lock.lock(); System.out.println("t1 start"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t1.start(); Thread t2 = new Thread(()->{ boolean lockDudge=false; try { //lock.lock(); //lock() method thread once started cannot interrupt the result: t1 start and then remain fixed //(Interruptable locks) can respond to the interrupt() method, which interrupts thread 2's wait lock.lockInterruptibly(); if(lock.tryLock()) lockDudge=true; System.out.println("t2 start"); TimeUnit.SECONDS.sleep(5); System.out.println("t2 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { if(lockDudge==true) lock.unlock(); } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); //(Main thread) Interrupts thread 2's wait because threads hold locks all the time, so t2 cannot get locks } }
ReentrantLock5
The default Synchronized is an unfair lock. Threads A, B,C all compete for the lock. A gets it, releases it after use, B waits a day, C waits a second, C gets the lock first
This is not fair, but it is better and more efficient for cou without computing thread wait times.
ReentrantLock can also be specified as a fair lock (who waits long and gets the lock), which is less efficient (waiting time needs to be calculated)
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLock5 extends Thread { private static ReentrantLock lock=new ReentrantLock(true); //The parameter true denotes a fair lock. Compare the output public void run() { for(int i=0; i<100; i++) { lock.lock(); try{ System.out.println(Thread.currentThread().getName()+"Acquire locks"); }finally{ lock.unlock(); } } } public static void main(String[] args) { ReentrantLock5 rl=new ReentrantLock5(); Thread th1=new Thread(rl); Thread th2=new Thread(rl); th1.start(); th2.start(); //Theoretically t1,t2 are locked alternately //But sometimes t1 gets it, and t2 gets it after 100 cycles of printing } }
summary
ReentrantLock provides Synchronized functionality and flexibility
(1) You can try to lock and specify a time
(2) Can be set as interruptable lock
(3) It can be set as fair lock, who waits for the time difference and who gets the lock first.