Lock
1.ReentrantLock类
1.使用ReentrantLock类也可以实现使用synchroized的互斥效果,并且使用起来有很多地方更灵活。
用法如下
class MyService{ //建立锁对象 private Lock lock=new ReentrantLock(); public void num(){ //加锁 lock.lock(); for(int i=0;i<10;i++){ System.out.println(i): } // 释放锁 lock.unlock(); } }
使用ReentrantLock在基本的功能上和synchroized一样,调用lock.lock(),就像获得了一个“对象监视器”,直到它lock.unlock()以后,其他线程才能获得执行权。
2.那么使用lock以后,怎么进行线程间通信呢?
关键字synchronized与wait()和notify()/notifyall()搭配使用可以实现等待/通知。
使用ReentrantLock也可以实现,但必须借助Condition。一个lock可以创建多个Condition对象。Condition下面有await和signal和signalAl方法与wait()和notify()/notifyall(),怎么用呢?
class MyService{ //建立锁对象 private Lock lock=new ReentrantLock(); //创建Condition对象. private Condition c=lock.newCondition(); public void awaitOne(){ //加锁 lock.lock(); System.out.println(A): c.await(); System.out.println(B): // 释放锁 lock.unlock(); } public void signalOne(){ //加锁 lock.lock(); c.signal(); // 释放锁 lock.unlock(); } }
需要注意:一定要在lock.lock()之后才可以await(),因为要首先获得监视器对象。等待通知必须建立在同步的基础上,如果不同步,就会存在竞态条件,就没有存在的意义了。
上面说了,一个lock可以建多个Condition对象
class MyService{ //建立锁对象 private Lock lock=new ReentrantLock(); //创建Condition对象. private Condition A=lock.newCondition(); private Condition B=lock.newCondition(); private Condition C=lock.newCondition(); public void awaitOne(){ //加锁 lock.lock(); System.out.println(A): A.await(); System.out.println(B): // 释放锁 lock.unlock(); } public void signalOne(){ //加锁 lock.lock(); A.signal(); // 释放锁 lock.unlock(); } public void awaitTwo(){ //加锁 lock.lock(); System.out.println(A): B.await(); System.out.println(B): // 释放锁 lock.unlock(); } public void signalTwo(){ //加锁 lock.lock(); B.signal(); // 释放锁 lock.unlock(); } public void awaitThree(){ //加锁 lock.lock(); System.out.println(A): C.await(); System.out.println(B): // 释放锁 lock.unlock(); } public void signalThree(){ //加锁 lock.lock(); C.signal(); // 释放锁 lock.unlock(); } }
仔细看看这个程序,有什么好处?
看明白吓了一跳,可以随意指定唤醒的对象了!这是synchroized做不到的,使用synchroized的时候相当于,创建了一个Condition对象,所以唤醒的时候是随机唤醒一个等待的线程。
用lock怎么实现生产者消费者模式呢?
class MyService{ private Lock lock=new ReentrantLock(); private Condition c=lock.newCondition(); private boolean isGet=false; public void set(){ lock.lock(); while(isGet==true){ c.await(); } System.out.pritln("*"); isGet=true; c.signalAll(); } public void get(){ lock.lock(); while(isGet==false){ c.await(); } System.out.pritln("&"); isGet=false; c.signalAll(); } }
3.公平锁与非公平锁
锁lock分为公平锁与非公平锁:公平锁获取锁的顺序是按照线程加锁的顺序来分配。非公平锁是一种获取锁的枪战机制,是随机获得锁的,先来的不一定先得到锁,这个方式可能会导致一些弱的线程一直抢不到锁,所以也就是不公平的了。
Lock lock=new ReentrantLock(boolean isFair);
通过这个设置是否是公平锁
公平锁运行起来如下图所示
可以看到先运行的先获得了锁。非公平锁呢?
先运行的不一定先获得锁。
4.介绍一下lock类的方法
getHoldCount()查询当前线程保持此锁定的个数,也就是调用lock()方法的次数!
getQueueLength() 返回正等待获取此锁定的线程数,也就是说有五个线程,一个获得了锁,剩下四个等。
getWaitQueueLength() 返回等待与此锁定相关的给定条件Condition的线程估计数,比如说5个线程,每个线程都执行了同一个Condition对象的await()方法,那就会返回5。
hasQueuedThread(Thread thread) 查询thread线程是否在等待获取锁
hasQueuedThreads 查询是否有线程正在等待获取锁
hasWaiters(Condition condition) 查询是否有线程正在等待与此锁定有关的condition条件。也就是查询有没有等待conditon对象的await()。
ifFair() 判断是不是公平锁
isHelpBycurrentThread() 查询当前线程是否保持此锁定
isLocked()查询此锁定是否由任意线程保持
lockInterruptibly() 如果当前线程未被中断,则获取锁,如果被锁定了,则抛出异常。
tryLock()仅在调用时锁定未被另一个线程保持时才获取该锁定。
tryLock(long timeout,TimeUnit unit)如果锁定在给定的时间内没有被其他的线程保持,且当前线程未被中断,则获取该线程。
awaitUninterruptibly() 使用这个代替await()可以在线程被interrput()的时候不抛出异常只停止运行
5.使用Condtion实现顺序执行
class Mysevice{ private static int isNum=1; private Lock lock=new ReentrantLock(); private Condition A=lock.newCondtion(); private Condition B=lock.newCondtion(); private Condition C=lock.newCondtion(); public static void main(String[] args){ Thread t1=new Thread(){ public void run(){ while(isNum!=1){ A.await(); } for(int i=0;i<3;i++){ System.out.println(Thread.currentend.getName()+i); } isNum=2; B.singalAll(); } }; Thread t2=new Thread(){ public void run(){ while(isNum!=2){ B.await(); } for(int i=0;i<3;i++){ System.out.println(Thread.currentend.getName()+i); } isNum=3; C.singalAll(); } }; Thread t3=new Thread(){ public void run(){ while(isNum!=3){ A.await(); } for(int i=0;i<3;i++){ System.out.println(Thread.currentend.getName()+i); } isNum=1; A.singalAll(); } }; } }
这就是使用Condition的好处,可以随机也可以按顺序来,synchroized是做不到的。
2.ReentrantReadWriteLock类
1.为什么要使用这个类呢?
因为ReentrantLock具有完全排他的效果,即同一时间只有一个在执行lock.lock()方法后面的任务,这样做虽然保证了安全,但是在一些不需要操作实例变量的场景下,这样效率就很低。所以使用读写锁。读写锁有什么优点呢?
class A{ private Lock lock=new ReentrantReadWriteLock(); public void read(){ //使用读锁 lock.readLock().lock(); //释放读锁 lock.readLock().unlock(); } public void write(){ //使用写锁 lock.writeLock().lock(); //释放写锁 lock.writeLock().unlock(); } }
读锁与读锁异步,读锁与写锁同步,写锁与写锁同步。写锁与读锁同步。也就是说,不会修改实例变量的情况下读锁与读锁异步,只提供了可见性