java 锁全面解析(二)

来源:互联网 发布:淘宝怎么给商品打折 编辑:程序博客网 时间:2024/06/17 08:59

接着上篇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可以提高多个线程进行读操作的效率。

class Resource {    private final int MAX_SIZE = 100;    private LinkedList<Object> list = new LinkedList<Object>();    private Lock lock = new ReentrantLock();    private Condition condition = lock.newCondition();    public void produce(int num) {        lock.lock();        try {            while (list.size() + num > MAX_SIZE) {                System.out.println("Thread " + Thread.currentThread() + " 需要生产已满,等待消费");                condition.await();            }            for (int i = 0; i < num; i++) {                list.add(new Object());            }            System.out.println("【已经生产产品数】:" + num + "【现仓储量为】:" + list.size());            condition.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }    public void consume(int num) {        lock.lock();        try {            while (list.size() < num) {                System.out.println("Thread " + Thread.currentThread() + " 消费不足,需要生产");                condition.await();            }            for (int i = 0; i < num; i++) {                list.remove();            }            System.out.println("【已经消费产品数】:" + num + "【现仓储量为】:" + list.size());            condition.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();        }    }}


五、ThreadLocal

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

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