咱们来看下ReentrantLock的基本用法
ThreadDomain35类java
public class ThreadDomain35 { private Lock lock = new ReentrantLock(); public void testMethod() { try { lock.lock(); for (int i = 0; i < 2; i++) { System.out.println("ThreadName = " + Thread.currentThread().getName() + ", i = " + i); } } finally { lock.unlock(); } } }
线程和main方法异步
public class MyThread35 extends Thread { private ThreadDomain35 td; public MyThread35(ThreadDomain35 td) { this.td = td; } public void run() { td.testMethod(); } public static void main(String[] args) { ThreadDomain35 td = new ThreadDomain35(); MyThread35 mt0 = new MyThread35(td); MyThread35 mt1 = new MyThread35(td); MyThread35 mt2 = new MyThread35(td); mt0.start(); mt1.start(); mt2.start(); } }
输出结果ide
ThreadName = Thread-2, i = 0 ThreadName = Thread-2, i = 1 ThreadName = Thread-0, i = 0 ThreadName = Thread-0, i = 1 ThreadName = Thread-1, i = 0 ThreadName = Thread-1, i = 1
一个线程必须执行完才能执行下一个线程,说明ReentrantLock能够加锁。函数
ThreadDomain37类,methodB用synchronized修饰this
public class ThreadDomain37 { private Lock lock = new ReentrantLock(); public void methodA() { try { lock.lock(); System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public synchronized void methodB() { System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName()); System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName()); } }
MyThread37_0类线程
public class MyThread37_0 extends Thread { private ThreadDomain37 td; public MyThread37_0(ThreadDomain37 td) { this.td = td; } public void run() { td.methodA(); } }
MyThread37_1类debug
public class MyThread37_1 extends Thread { private ThreadDomain37 td; public MyThread37_1(ThreadDomain37 td) { this.td = td; } public void run() { td.methodB(); } }
MyThread37_main方法code
public class MyThread37_main { public static void main(String[] args) { ThreadDomain37 td = new ThreadDomain37(); MyThread37_0 mt0 = new MyThread37_0(td); MyThread37_1 mt1 = new MyThread37_1(td); mt0.start(); mt1.start(); } }
运行结果以下对象
MethodA begin ThreadName = Thread-0 MethodB begin ThreadName = Thread-1 MethodB begin ThreadName = Thread-1 MethodA end ThreadName = Thread-0
加了synchronized依然是异步执行,说明ReentrantLock和synchronized持有的对象监视器不一样。ReentrantLock须要手动加锁和释放锁。rem
synchronized与wait()和nitofy()/notifyAll()方法能够实现等待/唤醒模型,ReentrantLock一样能够,须要借助Condition的await()和signal/signalAll(),await()释放锁。
ThreadDomain38类
public class ThreadDomain38 { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void await() { try { lock.lock(); System.out.println("await时间为:" + System.currentTimeMillis()); condition.await(); System.out.println("await等待结束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void signal() { try { lock.lock(); System.out.println("signal时间为:" + System.currentTimeMillis()); condition.signal(); System.out.println("signal等待结束"); } finally { lock.unlock(); } } }
MyThread38类,线程和main方法
public class MyThread38 extends Thread { private ThreadDomain38 td; public MyThread38(ThreadDomain38 td) { this.td = td; } public void run() { td.await(); } public static void main(String[] args) throws Exception { ThreadDomain38 td = new ThreadDomain38(); MyThread38 mt = new MyThread38(td); mt.start(); Thread.sleep(3000); td.signal(); } }
运行结果以下
await时间为:1563505465346 signal时间为:1563505468345 signal等待结束 await等待结束
能够看到,ReentrantLock和Condition实现了等待/通知模型。
一个Lock能够建立多个Condition;
notify()唤醒的线程是随机的,signal()能够有选择性地唤醒。
如今看一个利用Condition选择等待和唤醒的例子
ThreadDomain47类,定义add和sub方法
public class ThreadDomain47 { private final Lock lock = new ReentrantLock(); private final Condition addCondition = lock.newCondition(); private final Condition subCondition = lock.newCondition(); private static int num = 0; private List<String> lists = new LinkedList<String>(); public void add() { lock.lock(); try { while(lists.size() == 10) {//当集合已满,则"添加"线程等待 addCondition.await(); } num++; lists.add("add Banana" + num); System.out.println("The Lists Size is " + lists.size()); System.out.println("The Current Thread is " + "增长线程"); System.out.println("=============================="); this.subCondition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally {//释放锁 lock.unlock(); } } public void sub() { lock.lock(); try { while(lists.size() == 0) {//当集合为空时,"减小"线程等待 subCondition.await(); } String str = lists.get(0); lists.remove(0); System.out.println("The Token Banana is [" + str + "]"); System.out.println("The Current Thread is " + "减小线程"); System.out.println("=============================="); num--; addCondition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
MyThread40_0类,增长线程
public class MyThread40_0 implements Runnable { private ThreadDomain47 task; public MyThread40_0(ThreadDomain47 task) { this.task = task; } @Override public void run() { task.add(); } }
MyThread40_1类,减小线程
public class MyThread40_1 implements Runnable { private ThreadDomain47 task; public MyThread40_1(ThreadDomain47 task) { this.task = task; } @Override public void run() { task.sub(); } }
main方法,启动线程
public class MyThread40_main { public static void main(String[] args) { ThreadDomain47 task = new ThreadDomain47(); Thread t1=new Thread(new MyThread40_0(task)); Thread t3=new Thread(new MyThread40_0(task)); Thread t7=new Thread(new MyThread40_0(task)); Thread t8=new Thread(new MyThread40_0(task)); Thread t2 = new Thread(new MyThread40_1(task)); Thread t4 = new Thread(new MyThread40_1(task)); Thread t5 = new Thread(new MyThread40_1(task)); Thread t6 = new Thread(new MyThread40_1(task)); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); t8.start(); } }
输出结果以下
The Lists Size is 1 The Current Thread is 增长线程 ============================== The Lists Size is 2 The Current Thread is 增长线程 ============================== The Token Banana is [add Banana1] The Current Thread is 减小线程 ============================== The Token Banana is [add Banana2] The Current Thread is 减小线程 ============================== The Lists Size is 1 The Current Thread is 增长线程 ============================== The Token Banana is [add Banana1] The Current Thread is 减小线程 ============================== The Lists Size is 1 The Current Thread is 增长线程 ============================== The Token Banana is [add Banana1] The Current Thread is 减小线程 ==============================
能够看到,lists的数量不会增长太多,也不会减小太多。当集合满,使增长线程等待,唤醒减小线程;当集合空,使减小线程等待,唤醒增长线程。咱们用wait()/notify()机制没法实现该效果,这里体现了Condition的强大之处。
公平锁和非公平锁
ReentrantLock能够指定公平锁和非公平锁,公平锁根据线程运行的顺序获取锁,非公平锁则经过抢占得到锁,不按线程运行顺序。synchronized是非公平锁。在ReentrantLock(boolean fair)构造函数传入true/false来指定公平锁/非公平锁。
看个例子
ThreadDomain39类和main方法
public class ThreadDomain39 { private Lock lock = new ReentrantLock(true); public void testMethod() { try { lock.lock(); System.out.println("ThreadName" + Thread.currentThread().getName() + "得到锁"); } finally { lock.unlock(); } } public static void main(String[] args) throws Exception { final ThreadDomain39 td = new ThreadDomain39(); Runnable runnable = new Runnable() { public void run() { System.out.println("线程" + Thread.currentThread().getName() + "运行了"); td.testMethod(); } }; Thread[] threads = new Thread[5]; for (int i = 0; i < 5; i++) threads[i] = new Thread(runnable); for (int i = 0; i < 5; i++) threads[i].start(); } }
输出结果以下
线程Thread-0运行了 ThreadNameThread-0得到锁 线程Thread-1运行了 线程Thread-2运行了 ThreadNameThread-1得到锁 线程Thread-3运行了 线程Thread-4运行了 ThreadNameThread-2得到锁 ThreadNameThread-3得到锁 ThreadNameThread-4得到锁
能够看到公平锁得到锁的顺序和线程运行的顺序相同。公平锁尽量地让线程获取锁的顺序和线程运行顺序保持一致,再执行几回,可能不一致。
ReentrantLock构造函数传入false,输出结果以下:
线程Thread-0运行了 线程Thread-2运行了 线程Thread-4运行了 线程Thread-3运行了 ThreadNameThread-0得到锁 线程Thread-1运行了 ThreadNameThread-1得到锁 ThreadNameThread-2得到锁 ThreadNameThread-4得到锁 ThreadNameThread-3得到锁
非公平锁得到锁的顺序和线程运行的顺序不一样
获取当前线程调用lock()的次数,通常debug使用。
看个例子
public class ThreadDomain40 { private ReentrantLock lock = new ReentrantLock(); public void testMethod1() { try { lock.lock(); System.out.println("testMethod1 getHoldCount = " + lock.getHoldCount()); testMethod2(); } finally { lock.unlock(); } } public void testMethod2() { try { lock.lock(); System.out.println("testMethod2 getHoldCount = " + lock.getHoldCount()); } finally { lock.unlock(); } } public static void main(String[] args) { ThreadDomain40 td = new ThreadDomain40(); td.testMethod1(); } }
输出结果以下
testMethod1 getHoldCount = 1 testMethod2 getHoldCount = 2
能够看到,testMethod1()被调用了一次,testMethod2()被调用了两次,ReentrantLock和synchronized同样,锁都是可重入的。
getQueueLength()获取等待的线程数量,isFair()判断是不是公平锁。
ThreadDomain41类和main方法,Thread.sleep(2000)使第一个线程以后的线程都来不及启动,Thread.sleep(Integer.MAX_VALUE)使线程没法unlock()。
public class ThreadDomain41 { public ReentrantLock lock = new ReentrantLock(); public void testMethod() { try { lock.lock(); System.out.println("ThreadName = " + Thread.currentThread().getName() + "进入方法!"); System.out.println("是否公平锁?" + lock.isFair()); Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { final ThreadDomain41 td = new ThreadDomain41(); Runnable runnable = new Runnable() { public void run() { td.testMethod(); } }; Thread[] threads = new Thread[10]; for (int i = 0; i < 10; i++) threads[i] = new Thread(runnable); for (int i = 0; i < 10; i++) threads[i].start(); Thread.sleep(2000); System.out.println("有" + td.lock.getQueueLength() + "个线程正在等待!"); } }
输出结果以下
ThreadName = Thread-1进入方法! 是否公平锁?false 有9个线程正在等待!
ReentrantLock默认是非公平锁,只有一个线程lock(),9个线程在等待。
hasQueuedThread(Thread thread)查询指定线程是否在等待锁,hasQueuedThreads()查询是否有线程在等待锁。
看个例子
ThreadDomain41类和main方法,和上面例子相似,Thread.sleep(Integer.MAX_VALUE); 让线程不释放锁,Thread.sleep(2000);让第一个线程以后的线程都没法启动。
public class ThreadDomain42 extends ReentrantLock { public void waitMethod() { try { lock(); Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } finally { unlock(); } } public static void main(String[] args) throws InterruptedException { final ThreadDomain42 td = new ThreadDomain42(); Runnable runnable = new Runnable() { public void run() { td.waitMethod(); } }; Thread t0 = new Thread(runnable); t0.start(); Thread.sleep(500); Thread t1 = new Thread(runnable); t1.start(); Thread.sleep(500); Thread t2 = new Thread(runnable); t2.start(); Thread.sleep(500); System.out.println("t0 is waiting?" + td.hasQueuedThread(t0)); System.out.println("t1 is waiting?" + td.hasQueuedThread(t1)); System.out.println("t2 is waiting?" + td.hasQueuedThread(t2)); System.out.println("Is any thread waiting?" + td.hasQueuedThreads()); } }
输出结果以下
t0 is waiting?false t1 is waiting?true t2 is waiting?true Is any thread waiting?true
t0线程得到了锁,t0没有释放锁,致使t1,t2等待锁。
isHeldByCurrentThread()判断锁是否由当前线程持有,isLocked()判断锁是否由任意线程持有。
请看示例
ThreadDomain43类和main方法
public class ThreadDomain43 extends ReentrantLock { public void testMethod() { try { lock(); System.out.println(Thread.currentThread().getName() + "线程持有了锁!"); System.out.println(Thread.currentThread().getName() + "线程是否持有锁?" + isHeldByCurrentThread()); System.out.println("是否任意线程持有了锁?" + isLocked()); } finally { unlock(); } } public void testHoldLock() { System.out.println(Thread.currentThread().getName() + "线程是否持有锁?" + isHeldByCurrentThread()); System.out.println("是否任意线程持有了锁?" + isLocked()); } public static void main(String[] args) { final ThreadDomain43 td = new ThreadDomain43(); Runnable runnable0 = new Runnable() { public void run() { td.testMethod(); } }; Runnable runnable1 = new Runnable() { public void run() { td.testHoldLock(); } }; Thread t0 = new Thread(runnable0); Thread t1 = new Thread(runnable1); t0.start(); t1.start(); } }
输出结果以下
Thread-0线程持有了锁! Thread-1线程是否持有锁?false Thread-0线程是否持有锁?true 是否任意线程持有了锁?true 是否任意线程持有了锁?true
Thread-0线程testMethod方法持有锁,Thread-1线程testHoldLock方法没有lock操做,因此不持有锁。
tryLock()有加锁的功能,得到了锁且锁没有被另一个线程持有,此时返回true,不然返回false,能够有效避免死锁。tryLock(long timeout, TimeUnit unit)表示在给定的时间内得到了锁,锁没有被其余线程持有,且不处于中断状态。返回true,不然返回false;
看个例子
public class MyThread39 { public static void main(String[] args) { System.out.println("开始"); final Lock lock = new ReentrantLock(); new Thread() { @Override public void run() { String tName = Thread.currentThread().getName(); if (lock.tryLock()) { System.out.println(tName + "获取到锁!"); } else { System.out.println(tName + "获取不到锁!"); return; } try { for (int i = 0; i < 5; i++) { System.out.println(tName + ":" + i); } Thread.sleep(5000); } catch (Exception e) { System.out.println(tName + "出错了!"); } finally { System.out.println(tName + "释放锁!"); lock.unlock(); } } }.start(); new Thread() { @Override public void run() { String tName = Thread.currentThread().getName(); try { if (lock.tryLock(1,TimeUnit.SECONDS)) { System.out.println(tName + "获取到锁!"); } else { System.out.println(tName + "获取不到锁!"); return; } } catch (InterruptedException e) { e.printStackTrace(); } try { for (int i = 0; i < 5; i++) { System.out.println(tName + ":" + i); } } catch (Exception e) { System.out.println(tName + "出错"); } finally { System.out.println(tName + "释放锁!"); lock.unlock(); } } }.start(); System.out.println("结束"); } }
输出结果以下
开始 Thread-0获取到锁! Thread-0:0 Thread-0:1 Thread-0:2 Thread-0:3 Thread-0:4 结束 Thread-1获取不到锁! Thread-0释放锁!
Thread-0先得到了锁,且sleep了5秒,致使Thread-1获取不到锁,咱们给Thread-1的tryLock设置1秒,一秒内获取不到锁就会返回false。
若是Thread.sleep(0),那么Thread-0和Thread-1均可以得到锁,园友能够本身试下。
1.synchronized关键字是语法层面的实现,ReentrantLock要手动lock()和unlock(); 2.synchronized是不公平锁,ReentrantLock能够指定是公平锁仍是非公平锁; 3.synchronized等待/唤醒机制是随机的,ReentrantLock借助Condition的等待/唤醒机制能够自行选择等待/唤醒;