深入浅出lock

来源:互联网 发布:学设计软件机构 编辑:程序博客网 时间:2024/06/09 20:39

在JDK1.5以前,加锁方式通常为使用synchronized,jdk1.5出现了lock接口,提供了比synchronized更丰富的锁功能,

由于lock锁需要显示的加锁(lock)和显示的释放锁(unlock),因此又被称为显示锁

这种改变缺点是你要自己加锁释放锁了,麻烦了一点(可能会忘);

优点是你拥有了加锁解锁的可操作性,更加灵活,外加新增可中断锁、超时获取锁等多种功能。


灵活主要体现在你可以自由控制加锁区间,在多个加锁区域重叠时可以更加逻辑的写出代码。

lock的使用方式:

Lock lock=new ReentrantLock();lock.lock();try{//code}finally{lock.unlock();}
我们常在finally中关闭资源、释放锁,目的是使用finally块的特殊性,保证锁被释放。


lock具备了synchronized锁没有的特性

1、尝试非阻塞的获取锁;方法:tryLock();

2、能被中断地获取锁:与synchronized不同,获取到锁的线程会相应中断,中断异常抛出,同时锁被释放。方法:lockTnterruptibly();

3、超时获取锁:在指定的截止时间内获取锁,如果截止时间到了仍旧无法获取锁,则返回。方法:tryLock(long time,TimeUnit unit);


队列同步器(AQS):

Lock接口的实现基本上是聚合了一个同步器的子类来完成线程访问控制。

AQS是用来构建锁或其他同步组件的基础框架。

原理:它使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
使用方式:继承。子类通过继承同步器来实现它的抽象方法来管理同步状态。

状态更改:3个方法,分别是getState()、setState(int newState)、compareAndSwap(int except,int update).

这三个方法能够保证状态的改变是安全的。

子类推荐被定义为自定义同步组件的静态内部类。同步器自身没有实现任何同步接口,仅仅提供方法供实现。

同步器可以支持独占式的获取同步状态,也可以支持共享式的获取同步状态。

同步组件:ReenTrantLock、ReenTrantReadWriteLock、CountDownLatch。


同步器可重写的方法:

tryAcquired(int arg):独占式获取同步状态,使用该方法需要查询当前状态并判断同步状态是否符合预期,然后进行CAS设置同步状态。

tryRelease(int arg):独占式释放同步状态。

tryAcquiredShared(int arg):共享式的获取同步状态。

tryReleaseShared(int arg):共享式的释放同步状态。

isHeldExclusively():判断该方法是否被线程独占。


ReenTrangLock锁:

支持重进入的锁,即支持对资源的重复加锁。

它还提供对锁的公平与非公平性的选择。

公平性:先对锁进行请求的线程先被满足。即锁的获取时顺序的。

事实上,公平的所机制往往没有非公平的所机制效率来得高。

但是,公平锁能解决部分“饥饿”问题。

锁的可重入要求对所获取进行计数递增。,当计数等于0时说明被成功释放。


ReenTrantReadWriteLock:

前面的锁都是排它锁,读写锁维护了一对锁,读锁和写锁,通过分离两个锁,使得并发性能有了很大的提高。

在jdk 5之前,写操作和读操作要通过“等待通知机制”,就是当写操作开始时,所有晚于写操作的读操作都会进去等待状态,只有它完成并通知之后,才能执行读操作,以避免脏读。改用读写锁,只要在读时机啊还是那个读锁,写时加上写锁即可。

一把情况下,读写锁由于排它锁,因为大多数场景读时多于写的。

特性:

1、公平性选择;

2、重进入;读线程在此获取读锁,写线程可再此获取写锁、读锁。

3、锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级称为读锁。