设为首页 加入收藏

TOP

Java高并发之锁的使用以及原理浅析(二)
2019-08-13 05:36:25 】 浏览:129
Tags:Java 并发 使用 以及 原理 浅析
如果同一个线程多次获的锁,那么在释放是也要释放相同次数的锁。如果释放的锁少了,相当于该线程依然持有这个锁,那么其他线程就无法访问临界区了。释放的次数多了也会抛出java.lang.IllegalMonitorStateException异常。


      除了使用上的灵活,ReentrantLock还提供了一些高级功能如中断。限时等待等。


     对用synchrozide来说,如果一个线程在等待,那么结果只有两种情况,要么获得这把锁继续执行下去要么一直等待下去。而使用重入锁,提供了另外一种可能,那就是线程可以被中断。也就是说在这里可以取消对锁的请求。这种情况对解决死锁是有一定帮组的。


     下面代码产生了一个死锁,但是我们可以通过锁的中断,解决这个死锁。


        线程t1和t2启动后,t1先占用lock1然后在请求lock2;t2先占用lock2,然后请求lock1,因此很容易形成线程之间的相互等待。着这里使用的是ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。


      最后由于t2线程被中断,t2会放弃对lock1的1请求,同时释放lock2。这样可以使t1继续执行下去,结果如下图


   


   除了等待通知以外,避免死锁还有另外一种方式,那就是限时等待。通过给定一个等待时间,让线程自动放弃。


        tryLock有两个参数,一个表示等待时长,另一个表示计时单位。在这里就是通过lock.tryLock(5,TimeUnit.SECONDS)来设置锁申请等待限时,此例就是限时等待5秒获取锁。在这里的锁请求最多为5秒,如果超过5秒未获得锁请求,则会返回fasle,如果成功获得锁就会返回true。此案例中第一个线程会持有锁长达6秒,所以另外一个线程无法在5秒内获得锁 故案例输出结果为Gets lock failed


        另外tryLock方法也可以不带参数之直接运行,在这种情况下,当前线程会尝试获得锁,如果锁并未被其他线程占用,则申请锁直接成功,立即返回true,否则当前线程不会进行等待,而是立即返回false。这种模式不会引起线程等待,因此也不会产生死锁。


      下边展示了这种使用方式   


      使用了tryLock后,线程不会傻傻的等待,而是不同的尝试获取锁,因此,只要执行足够长的时间,线程总是会获得所有需要的资源。从而正常执行。下边展示了运行结果。表示两个线程运行都正常。


     


  在大多数情况下。锁的申请都是非公平的。也就是说系统只是会从等待锁的队列里随机挑选一个,所以不能保证其公平性。但是公平锁的实现成本很高,性能也相对低下。因此如果没有特别要求,也不需要使用公平锁。


    对上边ReentrantLock几个重要的方法整理如下。


     Conditon和ReentrantLock的组合可以让线程在合适的时间等待,或者在某一个特定的时间得到通知,继续执行。在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。


     使用案例如下


 


   结果如下



   这样看来,Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,下面引入API中的一段代码,加以说明。


      这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么要等待notFull信号才能继续put,如果得到notFull信号会像集合中添加元素,并且put操作会发出notEmpty的信号,而在其take方法中如果发现集合长度为空,那么会等待notEmpty的信号,接受到notEmpty信号才能继续take,同时如果拿到一个元素,那么会发出notFull的信号。


       信号量(Semaphore)为多线程协作提供了更为强大的控制用法。无论是内部锁Synchronized还是ReentrantLock,一次都只允许一个线程访问资源,而信号量可以多个线程访问同一资源。Semaphore是用来保护一个或者多个共享资源的访问,Semaphore内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。


     信号量的UML的类图如下,可以看出和ReentrantLock一样,Semaphore也包含了sync对象,sync是Sync类型;而且,Sync是一个继承于AQS的抽象类。Sync包括两个子类:"公平信号量"FairSync 和 "非公平信号量"NonfairSync。sync是"FairSync的实例",或者"NonfairSync的实例";默认情况下,sync是NonfairSync(即,默认是非公平信号量)


 


       信号量主要提供了以下构造函数


       这里,num指定初始许可计数。因此,它指定了一次可以访问共享资源的线程数。如果是1,则任何时候只有一个线程可以访问该资源。默认情况下,所有等待的线程都以未定义的顺序被授予许可。通过设置how为true,可以确保等待线程按其请求访问的顺序被授予许可。信号量的主要逻辑方法如下


      实例如下:这里我们模拟10个人去银行存款,但是该银行只有两个办公柜台,有空位则上去存钱,没有空位则只能去排队等待。最后输出银行总额


     ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问(也就

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java并发之阻塞队列浅析 下一篇Java中通过Hibernate-Validation..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目