java 锁全面解析(二)

接着上篇java 锁全面解析(一)

四、java.util.corrent包

Lock接口及其实现提供了与synchronized关键字类似的同步功能,与synchronized关键字相比,缺少了隐式释放锁的便捷,但是拥有锁获取和释放的可操作性、可中断的获取锁以及超时获取锁等多种同步特性。

1、Lock具备的特性

1)尝试非阻塞地获取锁

2)能被中断的获取锁

3)超时获取锁

常见的使用:

Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
2、Lock底层实现——队列同步器

    锁是面向使用者的,它定了使用者与交互的接口藏了实现细节;同步器面向的是实现者,它化了实现方式,屏蔽了同步状管理、线程的排、等待与醒等底操作。

    同步器的设计师基于模板方法模式的,继承者实现抽象方法来管理同步状态。同步器提供3个状态管理方法:getState(), setState(),compareAndSetState()。 同步器提供的模板方法基本上分 3 :独占式 取与 放同步状 、共享式 取与 放同步状 查询 同步 列中的等待 线 程情况。 同步器依赖内部的同步队列(双向FIFO队列)来完成同步状态的管理。当前线程获取同步状态失败时,会将当前线程以及等待状态等信息构成一个节点,加入到队列中,同时阻塞当前线程。同步状态释放的时候,会把首节点中的线程唤醒,使其尝试获取同步状态。
3、Lock基本操作
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

1) 重入锁ReentrantLock

指任意线程在获取锁之后能够再次获取该锁而不被阻塞。释放锁时,线程重复n次获取了锁,需要n次释放其他线程才能获取到锁。

2)公平锁

公平性是针对获取锁而言的。如果一个锁是公平的,那么锁的获取顺序准讯FIFO,但通常情况下公平锁的性能会比非公平锁要低。

3)读写锁

允许多个读线程同时 获取锁,但是在写线程获取锁时,所有的读和其他写线程都将被阻塞。

4)锁降级

锁降级是指写锁降级为读锁。

5)condition接口

Condition了等待/通知两种型的方法,当前线些方法,需要提前取到Condition象关锁。Condition对象由Lock对象创建,所以Condition也是依赖Lock对象的。在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll()。

4、Lock与synchronized的异同

     1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。


五、ThreadLocal

ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递。JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。

 先了解一下ThreadLocal类提供的几个方法:

1
2
3
4
public  T get() { }
public  void  set(T value) { }
public  void  remove() { }
protected  T initialValue() { }

简单来看,ThreadLocal类的实现是依托于一个内部类ThreadLocalMap,存取的时候,先获得当前线程,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值]。


在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

这种存储结构的好处:

1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。

2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。

关于ThreadLocalMap<ThreadLocalObject>弱引用问题:

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<nullObject>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。

1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。



     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值