【福利】关注微信公众号:深夜程猿,回复关键字即可获取学习视频、简历模版

欢迎读者批评指正~~
背景知识
在Java中,要想做到线程同步的传统方式是使用关键字synchronized。synchronized关键字提供了一个基本的同步机制,但是它的使用是十分严格的。例如,一个线程只能获取一次锁。同步的阻塞没有提供任何阻塞队列机制,当一个线程退出的时候,任何的线程都可以获取锁。这样会导致某些线程在一段长时间内处于饥饿状态(获取不到锁,无法访问资源)。可重入锁(Reentrant Lock)的引入使得解决这方面的问题变得简便,处理Java的线程同步问题也会加灵活。
什么是可重入锁
可重入锁实现了Lock接口,提供了一些共享资源的方法。操作共享资源的代码处于lock方法和unlock方法的调用之间。这使得当前工作线程获取锁,阻塞其他想要获取共享资源锁的线程。 正如名字那样,可重入锁使得线程可以多次进入资源锁(就是占有该资源的线程在为完全释放资源锁的时候,还可以再一次进入该资源锁)。当线程第一次进入锁的时候,会设置一个锁计数值(hold count)为1,在线程未释放锁的时候可以再一次进入该锁,锁计数值递增.对于每一次释放锁的请求,锁计数值会递减,直到锁计数值为0的时候,资源才会真正被释放。 可重入锁也会提供一个公平的参数,使得锁遵守锁的请求顺序。比如,在一个线程释放资源的时候,将锁分配给等待最长时间的线程。在构造可重入锁的时候,传入true就可以设置公平模式。 unlock方法要放在finally语句中调用,保证任何情况都会释放锁。
ReentrantLock的常用方法
lock()
调用lock方法,如果资源锁未被其它线程占用(当前线程占用还可以再一次获取锁)就获取资源锁,锁计数值递增,线程获得资源锁并立即;如果资源锁被其它线程占用,那么就进入休眠状态,直到获取到资源锁,同时锁计数值设为1;
tryLock()
调用tryLock方法,如果资源没有被其它任何线程占有(当前线程占用还可以再一次获取锁)就获取资源锁,返回true并递增锁计数值;如果资源锁被其它线程占用占用,返回false,线程不会退出,而是执行完run方法逻辑再尝试获取资源锁,依次下去,直到获取到资源锁;
tryLock(long timeout, TimeUnit unit)
作用和tryLock一样,只是为获取到资源锁的时候,线程会等待(不断尝试获取资源锁),超过设置的超时时间才会返回false;
lockInterruptibly()
调用这个方法,如果资源没有被其它任何线程占有(当前线程占用还可以再一次获取锁)就获取资源锁,递增锁计数值;如果再获取锁的过程,当前线程被其它线程interrupt(其它线程调用当前线程的interrupt()方法),那么就会要求处理InterruptedException;如果当前线程没有获取到锁,就会休眠。休眠过程,如果其它线程interupt当前线程,也会要求处理InterruptedException
unlock()
调用unlock方法会对锁计数值递减,只有锁计数值的值是0的时候才会释放资源
演示demo
public class Test {
static class Worker implements Runnable {
String name;
ReentrantLock re;
public Worker(ReentrantLock rl, String n) {
re = rl;
name = n;
}
public void run() {
boolean done = false;
while (!done) {
//获取外部锁
System.out.println("task name - " + name + " try to acquired lock by tryLock()");
boolean ans = re.tryLock();
if (ans) {
// 获取到锁
try {
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - " + name
+ " outer lock acquired at "
+ ft.format(d)
+ " Doing outer work");
Thread.sleep(1500);
// 获取内部锁
System.out.println("task name - " + name + " try to acquired lock by lock()");
re.lock();
try {
d = new Date();
ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("task name - " + name
+ " inner lock acquired at "
+ ft.format(d)
+ " Doing inner work");
System.out.println("Lock Hold Count - " + re.getHoldCount());
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放内部锁
System.out.println("task name - " + name +
" releasing inner lock");
re.unlock();
}
System.out.println("Lock Hold Count - " + re.getHoldCount());
System.out.println("task name - " + name + " work done");
done = true;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放外部锁
System.out.println("task name - " + name +
" releasing outer lock");
re.unlock();
System.out.println("Lock Hold Count - " +
re.getHoldCount());
}
} else {
// 没有获取到锁
System.out.println("task name - " + name +
" waiting for lock");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static final int MAX_T = 2;
public static void main(String[] args) {
ReentrantLock rel = new ReentrantLock();
ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
Runnable w1 = new Worker(rel, "Job1");
Runnable w2 = new Worker(rel, "Job2");
Runnable w3 = new Worker(rel, "Job3");
Runnable w4 = new Worker(rel, "Job4");
pool.execute(w1);
pool.execute(w2);
pool.execute(w3);
pool.execute(w4);
pool.shutdown();
}
}
输出
task name - Job1 try to acquired lock by tryLock()
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 05:29:50 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 try to acquired lock by lock()
task name - Job1 inner lock acquired at 05:29:51 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 try to acquired lock by tryLock()
task name - Job3 outer lock acquired at 05:29:53 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 try to acquired lock by lock()
task name - Job3 inner lock acquired at 05:29:54 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 try to acquired lock by tryLock()
task name - Job4 outer lock acquired at 05:29:56 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 try to acquired lock by lock()
task name - Job4 inner lock acquired at 05:29:57 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 try to acquired lock by tryLock()
task name - Job2 outer lock acquired at 05:30:00 Doing outer work
task name - Job2 try to acquired lock by lock()
task name - Job2 inner lock acquired at 05:30:01 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0