本文是笔者看了《实战java高并发程序设计》以后加上本身的理解所写的笔记。java
之因此直接从并发工具开始,是由于多线程的基础知识,例如多线程建立,经常使用的方法,以及synchronized,volatile关键字等知识以前学习的时候已经学习过许多遍了,可是java并发包却鲜有接触,此次决定写成博客,系列的总结一下。(以后有时间的话再把前面的知识补充上来,让这个系列更完整一些)多线程
Reentrantlock(重入锁) 并发
简单看一下它的用法:less
重入锁基本代码演示:jvm
package thread.thread_util; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 展现重入锁的用法 */ public class Lesson16_ReetrantLock { public static void main(String[] args) { Runnable target = new Runnable() { private Lock lock = new ReentrantLock(); @Override public void run() { lock.lock(); // lock.lock(); try { for (int i = 0; i < 5 ; i++) { System.out.println(Thread.currentThread().getId() + ": " + i); } } finally { lock.unlock(); // lock.unlock(); } } }; Thread t1 = new Thread(target); Thread t2 = new Thread(target); t1.start(); t2.start(); } }
重入究竟是什么意思?ide
相比synchronized关键字,重入锁显示的调用了加锁和解锁的时机,可是为何要叫重入锁呢?重入二字是什么意思呢?函数
重入的意思就是同一个线程能够反复进入同一个锁,上面的代码中就算把注释给去掉代码也是能够执行的。高并发
固然这个代码例子仍然不够贴切,谁会没事加两把锁上去呢?可是你能够想象一下递归,咱们都知道递归的每次操做都把相关的变量压入了一个栈之中,执行完成就弹出栈,而后再执行上一层函数,这里若是咱们进行了加锁操做的话,那么每一层函数在递推的时候就都会加上一层锁,而在回归的时候函数结束,则会释放锁。重入锁的内部有一个计数器,每加一个锁,计数器就加1,释放一个锁,计数器就减1,只有当计数器为0时,锁才能被释放。工具
咱们以经典的斐波那契数做为例子来展现一下。学习
深刻理解“重入”代码演示:
package thread.thread_util; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 递归中的重入锁 */ public class Lesson16_ReentrantLock02 { public static void main(String[] args) { Runnable target = new Runnable() { private Lock lock = new ReentrantLock(); @Override public void run() { System.out.println(Thread.currentThread().getId()); System.out.println("最终结果为: " + fibonacci(3)); } public int fibonacci (int n) { try { lock.lock(); System.out.println(Thread.currentThread().getId() + "得到了锁"); if( n == 1 ) return 1; if( n == 2 ) return 1; return fibonacci(n-1) + fibonacci(n-2); } finally { lock.unlock(); System.out.println(Thread.currentThread().getId() + "释放了锁"); } } }; Thread thread = new Thread(target); thread.start(); } }
结果:
11
11得到了锁
11得到了锁
11释放了锁
11得到了锁
11释放了锁
11释放了锁
最终结果为: 2
咱们能够看到这里总共得到了3次锁,释放了3次锁,整个锁才会被最终释放。
你能够再开一个线程进行验证,结果会相似下面这样:
11 11得到了锁 11得到了锁 11释放了锁 11得到了锁 11释放了锁 11释放了锁 最终结果为: 2 12 12得到了锁 12得到了锁 12释放了锁 12得到了锁 12释放了锁 12释放了锁 最终结果为: 2
重入锁的好基友:Condition对象
Condition的功能和Object.wait()与Object.notify()比较类似,它让咱们能够控制某个线程何时开始等待,何时被唤醒,而不是由jvm来决定。
条件对象代码演示:
package thread.thread_util; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 展现条件对象的基本用法 * await * signal * signalAll */ public class Lesson17_Condition01 implements Runnable{ public static Lock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); @Override public void run() { try { lock.lock(); condition.await(); System.out.println("Thread is going on"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) throws Exception{ Runnable target = new Lesson17_Condition01(); Thread thread = new Thread(target); thread.start(); Thread.sleep(2000); lock.lock(); condition.signal(); lock.unlock(); } }
这里在Runnable对象中,将对象锁住并让线程进入了休眠状态
主线程中进行了2秒钟的休眠,这2秒钟内子线程也没法执行,由于被“await()”了,直到2秒钟后,主线程再次开始执行,对线程进行了唤醒(signal)操做,唤醒操做也要进行加锁操做。
关于重入锁还有几点须要说明:
而对于重入锁来讲,还存在另一种状态,那就是中断线程,也就是把等待队列中的线程中断,那么这个中断有什么用呢?
好比说若是出现了死锁的状况,两个线程都在等待对方释放资源,那么这个时候若是我主动的中断一个线程,那么这个线程所持有的资源就被释放啦。咱们来看看代码
重入锁中断响应代码演示:
package thread.thread_util; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 展现重入锁的中断响应功能 * 首先构造一个死锁 * 而后中断某个线程,释放资源 * 最后另一个线程就能够得到释放的资源,完成线程的任务 */ public class Lesson16_ReentrantLock03 implements Runnable{ private static ReentrantLock lock1 = new ReentrantLock(); private static ReentrantLock lock2 = new ReentrantLock(); private int flag = 0; public void setFlag(int val) { this.flag = val; } @Override public void run() { try { if(flag == 1) { lock1.lockInterruptibly(); Thread.sleep(1000); lock2.lockInterruptibly(); System.out.println("Thread1 is going on "); } else { lock2.lockInterruptibly(); Thread.sleep(1000); lock1.lockInterruptibly(); System.out.println("Thread2 is going on "); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(lock1.isHeldByCurrentThread()){ lock1.unlock(); } if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } } } public static void main(String[] args) { Lesson16_ReentrantLock03 target1 = new Lesson16_ReentrantLock03(); Lesson16_ReentrantLock03 target2 = new Lesson16_ReentrantLock03(); target1.setFlag(1); target2.setFlag(2); Thread thread1 = new Thread(target1); Thread thread2 = new Thread(target2); thread1.start(); thread2.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); } }
Acquires the lock unless the current thread is interrupted. (文档说明)
最后线程2进行了中断,代码中的“else”里面的代码中的lockInterruptibly()方法就会使线程2 释放lock2并放弃对lock1的请求。
运行结果:
Thread1 is going on java.lang.InterruptedException at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335) at thread.thread_util.Lesson16_ReentrantLock03.run(Lesson16_ReentrantLock03.java:33) at java.lang.Thread.run(Thread.java:748) Process finished with exit code 0
锁申请等待时限(tryLock)
这是主动的中断,还有另一种更为简便的方法,就是给锁的申请添加时限,若是规定时间内我还没获得锁,那我就不要这把锁了
trylock() 代码演示:
package thread.thread_util; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; public class Lesson16_ReentrantLock04 implements Runnable{ private static ReentrantLock lock = new ReentrantLock(); @Override public void run () { try { if(lock.tryLock(3,TimeUnit.SECONDS)){ System.out.println(Thread.currentThread().getName() + " get lock successful"); Thread.sleep(4000); } else { System.out.println(Thread.currentThread().getName() + " get lock failed"); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { Lesson16_ReentrantLock04 target = new Lesson16_ReentrantLock04(); Thread t1 = new Thread(target); Thread t2 = new Thread(target); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
这里线程1先得到了锁,而后睡眠6秒钟(sleep并不会释放锁),因此线程2在3秒钟内都没法得到所最后进入else代码段。
最后结果:
线程1 get lock successful
线程2 get lock failed
关于重入锁和条件对象暂时就写到这里。